-
Notifications
You must be signed in to change notification settings - Fork 0
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
Test suite for client #8
Conversation
const app = mount(<App />, { | ||
mocks: [$.getAccountMock], | ||
route: "accounts/1/dashboard" | ||
}) |
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.
The mount
test helper renders a React component for testing. It sets up a provider to handle GraphQL queries originating from the component. It also sets up a LocationProvider
to provide the initial URL set up by the test, and to keep track of any URL changes that components make.
In most cases you will need to provide an array of mocks. Each of these is a predefined response to a GraphQL query. You will need to provide one mock for each query. If the same query runs more than once, you will need to specify a mock for each request.
}) | ||
|
||
it("archives a conversation from the conversation view", async () => { | ||
const history = createHistory( |
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.
Poodle uses @reach/router to render different React components when the URL changes. By default the router reads the location from window.location
. But we can provide a different history source for the router to read and update location. In tests we use a "memory source" which stores the location in a plain javascript object. In this test I create a history
object explicitly and pass it to mount
so that I can check history.location
after I expect the app to have changed the URL.
}) | ||
await updates(app) | ||
app.find("button[aria-label='archive']").simulate("click") | ||
await updates(app, 10) |
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.
Use the updates
helper when you need to allow for a delay for the component to update after an asynchronous process, such as a GraphQL request. You can provide a second argument to specify a longer delay, given in milliseconds.
setIsRead() | ||
} | ||
}, [data, setIsRead]) | ||
const setIsReadResult = useSetIsRead(data && data.conversation) |
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 decided to simplify the code here by replacing the graphql mutation and effect hook with a single invocation of a custom hook.
@@ -103,7 +103,7 @@ const useStyles = makeStyles(theme => ({ | |||
} | |||
})) | |||
|
|||
export default function Dashboard({ accountId }: Props) { | |||
export default function Dashboard({ accountId, navigate }: Props) { |
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 switched from using the imported navigate
function to getting navigate
from props. Dashboard
is a child component of a Router
component. Router
injects the navigate
prop into its children. The navigate
function in props uses the mock history set up in LocationProvider
, which is important for testing.
@@ -239,7 +240,7 @@ function SelectedActionsBar({ | |||
> | |||
<Toolbar className={classes.toolbar}> | |||
<span className={classes.title} /> | |||
<IconButton onClick={onArchive}> | |||
<IconButton aria-label="archive" onClick={onArchive}> |
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 an aria-label
here is useful for accessibility, and it also gives me a selector I can use to find the archive button in tests.
)} | ||
{conversations.map((conversation, index) => { | ||
return ( | ||
<React.Fragment key={conversation.id}> |
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 changed the key
prop from index
to conversation.id
. React uses the key
prop to match up elements that should be considered "the same" from one render to the next. When performing updates we want React to treat rows representing the same conversation as the same, and using the conversation ID as a key makes this work.
</List> | ||
</Paper> | ||
) | ||
} | ||
|
||
function ConversationRow({ |
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 split out a new component to display each row in the conversation list view. This way I can easily locate conversation rows in tests with app.find("ConversationRow")
.
import { MutationResult } from "react-apollo" | ||
import * as graphql from "../generated/graphql" | ||
|
||
export default function useSetisRead( |
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 is a custom hook; this pattern is highly useful for encapsulating logic to keep components as simple as possible. A custom hook is a function. The name always starts with use
so that ESLint can correctly make check uses of the hook.
Notice that a custom hook can call other hooks. Hooks only work when they are called either from function components or from custom hooks.
mounts = [] | ||
}) | ||
|
||
export function mount( |
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 is a helper for rendering React components for tests. Most of our components will only work correctly if they are rendered inside of an Apollo provider component that handles making GraphQL queries. In this case MockedProvider
sends back predefined responses instead of calling a real API. In addition LocationProvider
tells @reach/router to use the provided mock history object instead of using window.history
, which would attempt to use the real window location.
There is an afterEach
callback here that automatically unmounts any mounted components after each test.
🎉 This PR is included in version 1.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
🎉 This PR is included in version 1.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This change sets up Jest and Enzyme for testing React components. It includes fixtures and helpers to mock GraphQL requests.