Right and Wrong Answer Buttons
Tags: react, typescript, testing, tutorial
Published: 2020-01-17 04:45:22

In this post we will:
* Make a `Buttons` component that shows the `Submit` button and buttons that let the user record if their answer was right or wrong
* Make clicking the `Submit`, `Right`, and `Wrong` buttons advance to the next card
* Put the new `Buttons` component into `Answering`
* Hide the answer when we advance to the next card

In the next post we'll make a new context called `StatsContext`. We'll use `StatsContext` to track how many times the user answered a question right or wrong. We will also make a component to show the stats for the current question.

#`Buttons` Component

##User Story
* ... When the user is done with their answer, they click the submit button. The app shows them the answer key. The user compares their answer to the answer key. The user decides they got the question right, and clicks the 'right answer' button. Then the user sees the next question.

We don't just want to show the answer. We want the user to be able to say if their answer was right or if their answer was wrong. We need to show the user the Submit button before they are done answering the question. After the user clicks submit we need to show them the Right and Wrong buttons. Let's make the component to do that.

##Features
* after clicking the `Submit` button, `Right` and `Wrong` buttons show up
* clicking the `Right` or `Wrong` button moves to the next question

##Choose Components
We have already made the `Submit` button. We'll move it out of `Answering` and into our new `Buttons` component.

We'll use the [Button Group](https://react.semantic-ui.com/elements/button/#groups-group) from Semantic UI React for the `Right` and `Wrong` buttons. `Button.Group` can show buttons with some nice looking separators, like the word 'or' in a circular graphic.

![SUIR Button Group](https://thepracticaldev.s3.amazonaws.com/i/tsdob3laqzsgwzs0hbug.png)

##Decide What to Test
What are the important functions of these buttons? 
If the question has not been answered, then the Submit button should show up.
If the question has been answered, then the Right and Wrong buttons should show up.
Clicking the Right button should move to the next card.
Clicking the Wrong button should move to the next card.

##Writing the Tests for Buttons
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-1.tsx)

In the test file, write a comment line for each test you are going to write.
```javascript
//renders without crashing
//Buttons takes a prop answered: boolean 
//if !answered, then it should show a submit button
//if answered, then it should show right and wrong buttons
//clicking right advances to next card
//clicking wrong advances to next card
//clicking submit invokes submit, shows right and wrong buttons
```

Imports and `afterEach` go at the top.
```javascript
import React, { useState, useContext } from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Buttons from './index';
import { CardContext, CardProvider, initialState } from '../../../../services/CardContext';
import { CardState } from '../../../../types';

afterEach(cleanup);
```

##Helper Components
We are going to make `Buttons` dispatch actions to `CardContext` that will change the state of `CardContext` in ways that `Buttons` won't display. Clicking `Right` or `Wrong` will dispatch a `CardActionTypes.next` action, which should change the current index in `CardContext`. 

In order to make sure that `Buttons` is dispatching actions correctly, we'll make two helper components to render in our tests. `Current` will render the current index from `CardContext`. The other helper component will be `ButtonHolder`. `ButtonHolder` will be used instead of the `Answering` component, when we need a container to hold `Buttons` and `Current`.

###Helper Component: Current
Write the `Current` component. `Current` is a component that returns a div with the current index from `CardContext` in it. `Current` lets us see what the current index is, so we can test when the current index in `CardContext` has changed. This lets us test if other components in the app would show a different card when we click a button, but without the added complexity of actually importing those other components and knowing how they operate.

```javascript
//displays the current index from cardContext
//allows us to check if buttons can change current
const Current = () => {
    const { current } = useContext(CardContext);
    return <div data-testid='current'>{current}</div>
};
```

###Helper Component: ButtonHolder
Write the `ButtonHolder` component. This component will let us test `Buttons` the way our app will use it. Our app will use `Buttons` inside the `CardProvider`.

`Buttons` will take a boolean prop `answered`. `Buttons` will use the value of `answered` to decide whether to show `Submit` or `Right` and `Wrong`. 

Write a `useState` hook in `Buttonholder` to manage the value of `answered`. Remember, `useState` lets you store a value and gives you a function to change that value. This will let us test if clicking the `Submit` button invokes the submit function. It will also let us test if `Buttons` shows the `Submit` button when `answered` is *false*, and if `Buttons` shows the *Right* and *Wrong* buttons when `answered` is *true*.

`Buttons` needs to access the `CardContext` so it can change to the next card when the user clicks `Right` or `Wrong`. Put `Buttons` inside the `CardProvider`. Add `Current` inside the `CardProvider`. That way we can test if clicking `Right` and `Wrong` changes the current index.

`ButtonHolder` accepts two optional props, `answeredStartsAs` and `testState`. 

We will pass a value to `answeredStartsAs` when we want to override the starting value of `answered`. 

We will pass a `CardState` object to `testState` when we want to override the default initial state that the `CardProvider` starts with. 

```javascript
//a container component to hold  Buttons 
//submit() changes answered from false to true
const ButtonHolder = ({
    answeredStartsAs,
    testState
}:{
    answeredStartsAs?: boolean
    testState?: CardState
}) => {
    const [answered, setAnswered] = useState(answeredStartsAs !== undefined ? answeredStartsAs : false);

    return (
        <CardProvider testState={testState}>
            <Buttons answered={answered} submit={() => setAnswered(true)}/>
            <Current/>
        </CardProvider>
    )};
```
With the helper components written, we are ready to write the first test.

##Test 1: Renders Without Crashing
The first test is to make a component that will render without crashing. 

```javascript
//renders without crashing
it('renders without crashing', () => {
    render(<ButtonHolder/>);
});
```

##Pass Test 1: Render Without Crashing
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/index-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-1.tsx)

Let's write a Buttons component that will render without crashing.
We know we want Buttons to accept a boolean prop `answered` and a function `submit`. So we will declare those two props. We declare `submit`'s TypeScript type as `() => void`. The parentheses mean it's a function. This function does not accept any arguments, so the parentheses are empty. This function does not return a value. So the return value is void. We'll invoke the `submit` function in the `Buttons` component when the `Submit` button is clicked. 

Return a div.

```javascript
import React from 'react';

const Buttons = ({
    answered,
    submit
}:{
    answered: boolean,
    submit: () => void
}) => <div/>;

export default Buttons;
```
![Renders Without Crashing](https://thepracticaldev.s3.amazonaws.com/i/dpk1st3cfnv4ju2g1fvx.png)

###Test 2: When `answered` is *false*, `Buttons` Shows a `Submit` Button
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-2.tsx)

We'll render `Buttons` directly, without `ButtonHolder`. In this test we don't care about what `Buttons` does with the `CardContext`. We just want to know that the `Submit` button is on the screen.

We are using `getByText` because we expect the text 'Submit' to be found. Notice that we are using a [Regular Expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) \(RegEx\) to find the button. 

[jest.fn()](https://jestjs.io/docs/en/mock-functions) is a method that can replace functions in your tests. It makes a 'Mock Function.' Mock Functions can be a complicated topic. Basically, it's a fake function. We can use it in place of a real function, and if we wanted to, we could find out how many times the component had called it, and what arguments it received. We use a mock function here because `Buttons` needs a `submit` prop that is a function and we don't want to have to write a real function to pass to the `submit` prop.

```javascript
//Buttons takes a prop answered: boolean 
//if !answered, then it should show a submit button
it('has a submit Button', () => {
    const { getByText } = render(<Buttons answered={false} submit={jest.fn()}/>);
    const submit = getByText(/submit/i);
    expect(submit).toBeInTheDocument();
});
```

![Fails Submit Button](https://thepracticaldev.s3.amazonaws.com/i/4rxn9w8bbg16ugb8tjy2.png)

##Pass Test 2:  When `answered` is *false*, `Buttons` Shows a `Submit` Button
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/index-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-2.tsx)

Import the `Button` component from Semantic UI React.

```javascript
import { Button } from 'semantic-ui-react';
```

Change the return value from the div to a `Button` with `content = 'Submit'`. The `content` prop of a `Button` is the label text that shows up on the screen.

```javascript
}) => <Button content='Submit'/>;
```
![Passes Submit](https://thepracticaldev.s3.amazonaws.com/i/c2ytecxlfhzg1clrjdh4.png)

##Test 3: When `answered` is *true*, Should Show `Right` and `Wrong` Buttons
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-3.tsx)

Add a describe block named 'when answered is true'. This describe block will contain all the tests that cover when `answered` is *true* and we are using the `Right` and `Wrong` buttons.

```javascript
describe('when answered is true', () => {
//if answered, then it should show right and wrong buttons

    //clicking right advances to next card

    //clicking wrong advances to next card
});
```

Write the third test. We pass *true* as the value of answeredStartsAs so that the value of `answered` starts as *true*. `Buttons` should show the `Right` and `Wrong` buttons when `answered` is *true*. The `Right` and `Wrong` buttons have text labels, so we are finding them by searching for the text on them.

```javascript
describe('when answered is true', () => {
//if answered, then it should show right and wrong buttons
    it('shows right and wrong buttons', () => {
        const { getByText } = render(<ButtonHolder answeredStartsAs={true}/>);
        
        const right = getByText(/right/i);
        expect(right).toBeInTheDocument();
    
        const wrong = getByText(/wrong/i);
        expect(wrong).toBeInTheDocument();
    });

    //clicking right advances to next card

    //clicking wrong advances to next card
});
```

![Fails Right and Wrong](https://thepracticaldev.s3.amazonaws.com/i/z67qqof829y145279vcv.png)

##Pass Test 3: When `answered` is *true*, Should Show `Right` and `Wrong` Buttons
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match [src/scenes/Answering/components/Buttons/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-3.tsx)

Change the return value of `Buttons`. Write a `Button.Group` containing the `Right` and `Wrong` buttons. Use a [ternary operator](https://www.robinwieruch.de/conditional-rendering-react#ternary-operation-in-react) to return the Button.Group if `answered` is *true* and the `Submit` button if `answered` is *false*.

The ternary operator is a short way of writing an if/else statement. The ternary operator returns one value if a condition is true, and a different value if the condition is false. If the condition is true it returns the value after the ?, and if the condition is false it returns the value after the :.

```javascript
}) => answered
    ?   <Button.Group>
            <Button content='Right' positive />
            <Button.Or/>
            <Button content='Wrong' negative />
        </Button.Group>
    :   <Button content='Submit'/>;
```
![Passes Right and Wrong](https://thepracticaldev.s3.amazonaws.com/i/ie7wtu9mn2xkr9b8wdrs.png)

##Test 4: Clicking `Right` Changes to Next Card
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-4.tsx)

To test that clicking `Right` changes to the next card and that clicking `Wrong` changes to the next card we will pass a `CardState` object to override the default initialState. 

Declare the const `zeroState` inside the describe block. We'll use the spread operator to make `zeroState` a copy of the `initialState` object, but we will explicitly set `current` to 0. By doing this we make sure that our test starts with `current` at 0, even if the imported `initialState` object is changed and has `current` !== 0. The less you make your tests rely on code that is written outside of your tests, the more you can rely on your tests.


```javascript
describe('when answered is true', () => {
    //if answered, then it should show right and wrong buttons
    it('shows right and wrong buttons', () => {
        const { getByText } = render(<ButtonHolder answeredStartsAs={true}/>);
        
        const right = getByText(/right/i);
        expect(right).toBeInTheDocument();
    
        const wrong = getByText(/wrong/i);
        expect(wrong).toBeInTheDocument();
    });

    const zeroState = {
        ...initialState,
        current: 0
    };

    //clicking right advances to next card
```

Now write the test. Pass `zeroState` to `ButtonHolder` so that we know `current` will start as 0.

```javascript
  //clicking right advances to next card
    it('when the user clicks the Right button, the app changes to the next card', () => { 
        //pass testState with current === 0
        const { getByTestId, getByText } = render(<ButtonHolder answeredStartsAs={true} testState={zeroState}/>);
        
        //get the helper component Current
        const current = getByTestId('current');
        //current should show text 0
        expect(current).toHaveTextContent('0');

        //get the right button
        const right = getByText(/right/i);
        //click the right button
        fireEvent.click(right);

        expect(current).toHaveTextContent('1');
    });
```
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/y9arwi0wvtetqlj02xhb.png)

##Pass Test 4: Clicking `Right` Changes to Next Card
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/Scenes/Answering/components/Buttons/complete/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-4.tsx)

Import `useContext` from React. We'll be dispatching actions to the `CardContext` when the user clicks buttons. Import `CardActionTypes` from types.ts. Import `CardContext`.

```javascript
import React, { useContext } from 'react';
import { Button } from 'semantic-ui-react';
import { CardActionTypes } from '../../../../types';
import { CardContext } from '../../../../services/CardContext';
```

Change `Buttons` to get `CardContext`'s `dispatch` from `useContext`. Add an onClick function to the `Right` Button. The onClick function will dispatch an action with type of `CardActionTypes.next`.

```javascript
const Buttons = ({
    answered,
    submit
}:{
    answered: boolean,
    submit: () => void
}) => {
    const { dispatch } = useContext(CardContext);

    return answered
    ?   <Button.Group>
            <Button content='Right' positive 
                onClick={() => dispatch({ type: CardActionTypes.next })}
            />
            <Button.Or/>
            <Button content='Wrong' negative />    
        </Button.Group>
    :   <Button content='Submit'/>
};
```

![Passes Right Card Change](https://thepracticaldev.s3.amazonaws.com/i/03sjh9hvxwwjf74wb29z.png)

Next we will test that clicking the `Wrong` button changes the current index. Before you look at the example, try to write the test. Hint: it is based on the test for the `Right` button.

How do you think we will make the `Wrong` button pass the test? 

##Test 5: Clicking `Wrong` Changes to Next Card
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-5.tsx)

Write the test for clicking the `Wrong` Button. It is almost the same as the test for clicking the `Right` Button.

```javascript
   //clicking wrong advances to next card
    it('when the user clicks the Wrong button, the app changes to the next card', () => { 
        //pass testState with current === 0
        const { getByTestId, getByText } = render(<ButtonHolder answeredStartsAs={true} testState={zeroState}/>);
        
        //get the helper component Current
        const current = getByTestId('current');
        //current should show text 0
        expect(current).toHaveTextContent('0');

        //get the wrong button
        const wrong = getByText(/wrong/i);
        //click the wrong button
        fireEvent.click(wrong);

        expect(current).toHaveTextContent('1');
    });
```

![Fails Wrong Button](https://thepracticaldev.s3.amazonaws.com/i/xu8lx9rw8a6grv735asv.png)
 
##Pass Test 5: Clicking `Wrong` Changes to Next Card
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/Scenes/Answering/components/Buttons/complete/index-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-5.tsx)


Add an onClick function to the `Wrong` button. Dispatch an action with type `CardActionTypes.next`.

```javascript
            <Button content='Wrong' negative 
                onClick={() => dispatch({ type: CardActionTypes.next })}
            />    
```

![Passes Clicking Wrong Button](https://thepracticaldev.s3.amazonaws.com/i/1u5ctkjz3xrrq7brtlqk.png)


##Test 6: Clicking `Submit` shows `Right` and `Wrong` Buttons
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/Scenes/Answering/components/Buttons/complete/test-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-6.tsx)


The last test we'll do on `Buttons` is clicking the `Submit` button should show the `Right` and `Wrong` buttons. In the app, and inside the `ButtonHolder` component, clicking the `Submit` button will invoke the `submit` function passed as a prop to `Buttons`. In `Answering`, the `submit` function will set the value of `answered` to `true`.

Before we simulate the click event, we use `queryByText` to look for 'Right' and 'Wrong,' and we expect the results to be null.

After we simulate the click event, we use `getByText` to look for 'Right' and 'Wrong,' and we expect the results to be elements in the document.

```javascript
//clicking submit invokes submit, shows right and wrong buttons
it('clicking submit shows right and wrong', () => {
    const { getByText, queryByText } = render(<ButtonHolder />)
    
    const submit = getByText(/submit/i);
    expect(submit).toBeInTheDocument();
    
    expect(queryByText(/right/i)).toBeNull()
    expect(queryByText(/wrong/i)).toBeNull()

    fireEvent.click(submit);

    expect(queryByText(/submit/i)).toBeNull();
    expect(getByText(/right/i)).toBeInTheDocument();
    expect(getByText(/wrong/i)).toBeInTheDocument();
});
```

![Clicking Submit Fails](https://thepracticaldev.s3.amazonaws.com/i/d9jqunhxd1ctt066imyx.png)

##Pass Test 6: Clicking `Submit` shows `Right` and `Wrong` Buttons
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/Scenes/Answering/components/Buttons/complete/index-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-6.tsx)


Add an onClick function to the `Submit` button. The onClick function invokes the `submit` function passed as a prop to `Buttons`.

```javascript
<Button content='Submit' onClick={() => submit()}/>
```

![Click Submit Passes](https://thepracticaldev.s3.amazonaws.com/i/gadaqql5m6ip72twspic.png)

#Add Buttons to Answering
Now it is time to add `Buttons` to `Answering`.

##Choose Components
We are adding the *Buttons* component that we just wrote. We will also remove the old *Submit* Button from Answering.

##Decide What to Test
* Does clicking the *Submit* Button still show the answer?
* *Right* Button does not show up until the *Submit* Button is clicked
* *Wrong* Button does not show up until the *Submit* Button is clicked 
* Clicking the *Submit* Button makes the *Right* and *Wrong* Buttons show up

##Answering Tests 1-2: `Right` and `Wrong` Do Not Show Up Before `Submit` is Clicked
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-12.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/test-12.tsx)

We already test if clicking the `Submit` button shows the answer. We'll see if that test still passes when we import the `Buttons` component or not. 

We need to add a test that clicking the `Submit` button makes the `Right` and `Wrong` buttons show up.


Add a describe block near the bottom but above the snapshot test. Name the describe block 'clicking the Submit Button makes the Right and Wrong Buttons show up.' Write a comment inside the describe block for each test we are about to write.

```javascript
describe('clicking the Submit Button makes the Right and Wrong Buttons show up', () => {
    //the Right button does not show up before Submit is clicked
    
    //the Wrong button does not show up before Submit is clicked
    
    //Clicking Submit makes the Right Button show up
    
    //Clicking Submit makes the Wrong Button show up

});
```

Inside the describe block write two tests to make sure the *Right* and *Wrong* Buttons don't show up before *Submit* is clicked. We expect these tests to both pass, so we're writing them together.

```javascript
    //the Right button does not show up before Submit is clicked
    it('the Right button does not show up before Submit is clicked', () => {
        const { queryByText } = renderAnswering();
        const right = queryByText(/right/i);
        expect(right).toBeNull();
    });

    //the Wrong button does not show up before Submit is clicked
    it('the Wrong button does not show up before Submit is clicked', () => {
        const { queryByText } = renderAnswering();
        const wrong = queryByText(/wrong/i);
        expect(wrong).toBeNull();
    });
```
These tests should pass.

![Right and Wrong don't show up](https://thepracticaldev.s3.amazonaws.com/i/lf25u6qgwlplcjl5haq9.png)

Hopefully they will still pass when we add `Buttons` to `Answering`. Look at the queries that we used to search for the `Right` and `Wrong` buttons. If you wanted to be sure that these tests worked, what would you do? How would you change the `Answering` component to make these tests fail?

##Answering Tests 3-4: `Right` and `Wrong` Show Up After Submit is Clicked
Inside the describe block write two tests to make sure the *Right* and *Wrong* Buttons show up after *Submit* is clicked. Find the submit button and use fireEvent.click() to simulate the click event. Then find the `Right` or `Wrong` button using getByText.

```javascript
    //Clicking Submit makes the Right Button show up
    it('clicks the submit button and shows the Right button', () => {    
        const { getByText } = renderAnswering();
        
        //find the submit button
        const submit = getByText(/submit/i);
        //simulating a click on the submit button
        fireEvent.click(submit);

        const right = getByText(/right/i);
        expect(right).toBeInTheDocument();
    });

    //Clicking Submit makes the Wrong Button show up
    it('clicks the submit button and shows the Wrong button', () => {    
        const { getByText } = renderAnswering();
        
        //find the submit button
        const submit = getByText(/submit/i);
        //simulating a click on the submit button
        fireEvent.click(submit);

        const wrong = getByText(/right/i);
        expect(wrong).toBeInTheDocument();
    });
```

These tests should fail. `Right` and `Wrong` won't start showing up until we add `Buttons` to `Answering`.

![Right and Wrong fail to show up](https://thepracticaldev.s3.amazonaws.com/i/6k8xvhf4k9q8tq7f985y.png)

##Answering Pass Tests 1-4: `Right` and `Wrong` Show Up After Submit is Clicked
>File: src/scenes/Answering/index.tsx
>Will Match: [src/scenes/Answering/complete/index-9.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-9.tsx)

Ok, now let's add `Buttons` to `Answering`. 
Import `Buttons`.
```javascript
import Buttons from './components/Buttons';
```

Change the component. Delete the old `Submit` Button from the `Container`:
```javascript
    <Container data-testid='container' style={{position: 'absolute', left: 200}}>
         <Header data-testid='question' content={question}/>
         <Button>Skip</Button>
         <Form>
            <TextArea data-testid='textarea'/>
        </Form>
        <Button onClick={() => setShowAnswer(true)}>Submit</Button>
        <Answer visible={showAnswer}/>
    </Container>
```

And replace it with `Buttons`. Pass `showAnswer` as the prop `answered`, and make `submit` call `setShowAnswer(true)`.

```javascript
    <Container data-testid='container' style={{position: 'absolute', left: 200}}>
         <Header data-testid='question' content={question}/>
         <Button>Skip</Button>
         <Form>
            <TextArea data-testid='textarea'/>
        </Form>
        <Buttons answered={showAnswer} submit={() => setShowAnswer(true)}/>
        <Answer visible={showAnswer}/>
    </Container>
```

Save it. Now *Answering* will pass all the tests.
![All Pass](https://thepracticaldev.s3.amazonaws.com/i/5rs879q4hr919ia7ll19.png)

##Look at your app
Run the app with the command `npm start`. Click `Submit` and the answer shows up. The `Right` and `Wrong` buttons show up. 
![Buttons Are Here](https://thepracticaldev.s3.amazonaws.com/i/uiej2i929j007ok6oicj.gif)

Clicking the `Right` button will advance to the next card. Clicking the `Wrong` button will advance to the next card. Clicking the `Skip` button will also advance to the next card.

##Hiding the Answer When the Card Changes
You will notice that when you click `Submit`, the Answer opens but does not close when you move to the next card. That's not what we want. We need to change `Answering` so that it will hide the answer when the card changes. To hide the answer, we need to set `showAnswer` to *false* when the value of `current` changes. To do that, we will use the `useEffect` hook.

The [useEffect](https://reactjs.org/docs/hooks-effect.html) hook lets us run a function when one of the values that we tell useEffect to watch changes. We'll tell our `useEffect` to watch `current`. We'll make the code inside useEffect call `setShowAnswer(false)` when `current` changes. Then `Answer` will become invisible when `current` changes.

##Answering Test 5: Answer is Hidden when Card Changes
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-13.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/test-13.tsx)

Write the test for the answer going away inside of the describe block named 'submit button controls display of the answer.' Find and click the `Submit` button to show the answer. To check if the answer shows up, use the `compareToInitialAnswer` function we wrote earlier. Once you know the answer shows up, then find and click the `Skip` button. 

When you click the `Skip` button, the current index in `CardContext` will change. We'll search for the first answer again to make sure it isn't still showing up in the document. You should also write a `compareToSecondAnswer` function so that we can search for the answer from the second card. Make sure the second answer isn't shown either.
 

```javascript
  //answer goes away
  it('answer disappears when card changes', () => {
    const { getByText, queryByText } = renderAnswering();
    
    //find the submit button
    const submit = getByText(/submit/i);
    //simulating a click on the submit button
    fireEvent.click(submit);
  
    //use a custom function to find the answer
    const answer = getByText(compareToInitialAnswer);
    
    //assertion
    expect(answer).toBeInTheDocument();

    //clicking skip changes the current index 
    const skip = getByText(/skip/i);
    fireEvent.click(skip);

    //the answer to the second question
    const secondAnswer = initialState.cards[initialState.current + 1].answer;
    
    //remove lineBreaks from initialAnswer for comparison to textContent of elements 
    const withoutLineBreaks = secondAnswer.replace(/\s{2,}/g, " ");

    //a function that compares a string to the second answer
        const compareToSecondAnswer = (
            content: string, 
            { textContent } : HTMLElement
        ) => !!textContent && 
            textContent
            .replace(/\s{2,}/g, " ")
            .slice(6, textContent.length) === withoutLineBreaks;

    //look for the first answer
    const gone = queryByText(compareToInitialAnswer);
    //first answer shouldn't show up
    expect(gone).toBeNull();

    //look for the second answer
    const answer2 = queryByText(compareToSecondAnswer);
    //second answer shouldn't show up
    expect(answer2).toBeNull();
  });
```

![Answer Shows Up](https://thepracticaldev.s3.amazonaws.com/i/p64bxinydb9t4aussgup.png)
Test fails because the answer is still showing up.

##Pass Answering Test 5: Answer is Hidden when Card Changes
>File: src/scenes/Answering/index.tsx
>Will Match: [src/scenes/Answering/complete/index-10.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-10.tsx)

Write the `useEffect`. `useEffect` takes two arguments. 

The first argument is a function. We will write a function that uses `setShowAnswer` to set the value of `showAnswer` to *false*.
 
The second argument is an array. This array contains all the values that `useEffect` 'depends' on. It is called the array of 'dependencies.' Basically it means that when one of those values changes, `useEffect` will run the function in its first argument.

We'll include `current` in the dependency array. We put `current` in there because we want the code to run when `current` changes. 

We will also put `setShowAnswer` in the dependency array. The function `setShowAnswer` won't change and trigger the `useEffect` to run, but it is a value from outside the `useEffect` that we are using inside the `useEffect`. So we should put it in there. To find out why, click [here](https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies), but it is complicated and not something you need to know to get this app working.
 
This `useEffect` makes it so when the value of `current` changes, `showAnswer` will be set to *false*. So now when the user switches cards, they won't see the answer anymore.

Import `useEffect`.
```javascript
import React, { useState, useContext, useEffect } from 'react';
```

Write a `useEffect` hook to `setShowAnswer` to *false* when current changes. See how the first argument is a function, and the second argument is the array of dependencies?

```javascript
    useEffect(() => {
        //hide the answer
        setShowAnswer(false);
        
        //useEffect triggers when the value of current changes
    }, [current, setShowAnswer]);
  
return (
```

Ok! When the value of `current` changes the code inside the `useEffect` will trigger and set `showAnswer` to `false`. Save and run the tests. 

![Answer Shows Up](https://thepracticaldev.s3.amazonaws.com/i/7v743ukxdrdnrp6vvxsc.png)

What? Run the app and take a look at it. Click `Submit`. Click `Skip`. The answer disappears! So what's the problem?

####What's going on here?
Take a look at it through the React Dev Tools in your browser. When you click, you can see that the `Answer` is still getting rendered for a split second after the card changes. See the header here? 
![Dev Tools](https://thepracticaldev.s3.amazonaws.com/i/94wiivxlw3wvd9bp9pbd.png)
So we need to test things a bit differently. We need our test to wait for the `Header` to go away.

React Testing Library gives us [waitForElementToBeRemoved](https://testing-library.com/docs/dom-testing-library/api-async#waitforelementtoberemoved). It pretty much does what it says. You don't pass it a reference to an element. You give `waitForElementToBeRemoved` the query that you want to use, and it will try the query repeatedly for up to 4500 milliseconds. It will stop when the element is removed or it times out.

##Change Answering Test 5: Answer is Hidden when Card Changes
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-14.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-14.tsx)

Import `waitForElementToBeRemoved`.
```javascript
import { render, cleanup, fireEvent, waitForElementToBeRemoved } from '@testing-library/react';
```

Mark the test callback function as [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Async means we are using [asynchronous code](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous). Async code is a [complicated topic](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Concepts), and not one that you need to understand to get this app working. But basically, normal JavaScript code all executes one command after the other. If you want to run code a and then code b, you have to run code a, wait for a to finish, before you can then start to run code b. But asynchronous code is code that can be executed while other code is running. So if code a were asynchronous, you could start code a, and while code a is running, you could tell code b to start.
 
Making it an async function will allow us to use [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await). Await is one of several ways that javascript has for dealing with async code. Using the await command basically means we are waiting for the async code to get done running before running the next line of code that depends on the results of the async code. 

####Don't worry if you didn't understand that!
We can use await to wait for waitForElementToBeRemoved to finish running.

```javascript
 //answer goes away
    it('answer disappears when card changes', async () => {
```

Change the last lines of the test. Originally, we looked for answer2 and expected it to be null:
```javascript
//a function that compares a string to the second answer
    const compareToSecondAnswer = (content: string) => content === withoutLineBreaks;

    //look for the first answer
    const gone = queryByText(compareToInitialAnswer);
    //first answer shouldn't show up
    expect(gone).toBeNull();

    //look for the second answer
    const answer2 = queryByText(compareToSecondAnswer);
    //second answer shouldn't show up
    expect(answer2).toBeNull();
```

We'll change it to awaiting `waitForElementToBeRemoved`. Pass `waitForElementToBeRemoved` an anonymous function that uses `queryByText()` and our custom searching function `compareToSecondAnswer`. 

Remember, `queryByText` looks at each element and passes the text content to the function `compareToSecondAnswer`. `compareToSecondAnswer` compares each string that `queryByText` passes it to the second answer and returns true if it gets a match.

So what will happen here is `waitForElementToBeRemoved` will run `queryByText(compareToSecondAnswer)`. It will get an element back, because the second answer starts out in the document. That's why our first version of the test failed, because the search result for the second answer wasn't null. But `waitForElementToBeRemoved` will keep running that `queryByText` until it gets a null result. 

Once our `Answer` component finishes animating off, it unmounts its contents. Remember when we put the `Transition` into `Answer` and had to pass it the unmountOnHide prop? Passing this test is why we had to do that. When `Answer` unmounts its contents, `queryByText(compareToSecondAnswer)` will return null, and `waitForElementToBeRemoved` will pass.
 
```javascript
//a function that compares a string to the second answer
        const compareToSecondAnswer = (
            content: string, 
            { textContent } : HTMLElement
        ) => !!textContent && 
            textContent
            .replace(/\s{2,}/g, " ")
            .slice(6, textContent.length) === withoutLineBreaks;

        //look for the first answer
        const gone = queryByText(compareToInitialAnswer);
        //first answer shouldn't show up
        expect(gone).toBeNull();

        //second answer should go away
        await waitForElementToBeRemoved(() => queryByText(compareToSecondAnswer));
```
![Await Passes](https://thepracticaldev.s3.amazonaws.com/i/cixp4md9ohjnhep29q47.png)

Do you notice anything different about the test 'answer disappears when card changes?' Look at how long it took the tests to pass. Waiting for the `Transition` component to animate made the test take 1052 ms. That is a lot longer than the rest of the tests.

##One Last Thing: Clear the TextArea

If you tried typing an answer in the `TextArea`, you no doubt noticed that it does not get cleared out when you click `Skip`, `Right`, or `Wrong`. Let's fix that with the React `useEffect` hook.

##Answering Test 6: TextArea Clears When Current Changes
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-15.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/test-15.tsx)

Make a test that puts text in the `TextArea`, clicks the `Skip` button, then expects the `TextArea` to be empty.

We use [fireEvent.change()](https://testing-library.com/docs/dom-testing-library/api-events#fireeventeventname) to put text into the `TextArea`. `fireEvent.change()` takes two arguments. 

The first argument is a reference to the element.

The second argument is an object that describes the properties of the event. This event affects the target, which is the element that we passed as the first argument. The property of the target that is being changed is the value. We are setting the value property of the target element to the placeholder text.

```javascript
it('clears the answer when card changes', () => {
  const { getByText, getByTestId } =  renderAnswering();
  const textarea = getByTestId('textarea');

  const placeholder = 'placeholder text'
  //put the placeholder text into the textarea
  fireEvent.change(textarea, { target: { value: placeholder } });

//make sure the placeholder text is there
  expect(textarea).toHaveTextContent(placeholder);

//get the skip button
  const skip = getByText(/skip/i);
//click skip, this dispatches a 'next' action to cardcontext
//which should change the value of current 
//and trigger useEffect hook to clear the textarea
  fireEvent.click(skip);

//textarea should be empty
    expect(textarea).toHaveTextContent('');
});
```
![TextArea fails](https://thepracticaldev.s3.amazonaws.com/i/fxugsftcpo7f36j8p81t.png)

You might notice that the test isn't failing because textarea didn't clear. The test is failing because textarea doesn't contain the placeholder text. I found that the Semantic UI React TextArea doesn't work with fireEvent unless it is a [controlled component](https://reactjs.org/docs/forms.html#controlled-components). A controlled component is a component where the value is held in state. We need to make TextArea into a controlled component to be able to clear it out when the card changes anyway, so the test will work. But that is the kind of thing that you will encounter when you are trying to test your components.

##Pass Answering Test 6: TextArea Clears When Current Changes
>File: src/scenes/Answering/index.tsx
>Will Match: [src/scenes/Answering/complete/index-11.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-11.tsx)

To make `fireEvent.change()` work on the Semantic UI React `TextArea`, we need the `TextArea` to be a controlled component. Normally, you don't want to change your code to pass your tests. But we need the TextArea to be a controlled component anyway, so there is no problem with doing that now.

Add a `useState` hook to keep track of the value of `input`. Set it to an empty string to start with. Notice that because the starting value is a string, TypeScript will infer that `input` is a string and the function `setInput` takes a string as an argument.

Inside the `useEffect` hook that resets `showAnswer` to *false*, add a call to `setInput`. Set the input back to an empty string. Add `setInput` to the dependency array. Even though the `setInput` function won't change and trigger the `useEffect`, it is still a value from outside the `useEffect` that we are using inside the `useEffect`.

```javascript
///the value of the textarea where the user types their input
const [input, setInput] = useState('');

    useEffect(() => {
        //hide the answer
        setShowAnswer(false);
        
        //clear the TextArea
        setInput('');

        //useEffect triggers when the value of current changes
    }, [current, setShowAnswer, setInput]);
```

Make the `TextArea` a controlled component by setting its value prop equal to input. Write the onChange function. You can see the props of the SUIR TextArea [here](https://react.semantic-ui.com/addons/text-area/). The onChange function fires with two arguments. 

The first argument, that we call e, is the [React SyntheticEvent](https://reactjs.org/docs/events.html) object. We're not using it, so we don't even bother typing it. We just tell TypeScript to designate it as 'any.' If we wanted to type it, its type is SyntheticEvent. 

The second argument is all the props and the event value. We use Object Destructuring to pull the value out of the second argument. Notice that we don't have to give the second argument a name or deal with the whole object at all, we can just pull the value out and look at it.

The value can be a few different types. But remember how we let TypeScript infer the type for `setInput`? Well, we can only pass strings to `setInput`. So in th onChange function, before we call `setInput` and pass it the value from the `onChange` event, we assert that the typeof the value is string. If the type of the value is a string, then we call setInput. If the type is something else, then we don't call setInput.

```javascript
            <TextArea data-testid='textArea'
            value={input}
            onChange={(e: any, {value}) => typeof(value) === 'string' && setInput(value)}/>
```
![TextArea Passes](https://thepracticaldev.s3.amazonaws.com/i/q03k7n6mglljkw44ryur.png)

Now try writing an answer then clicking `Skip`, `Right`, or `Wrong`. The useEffect will trigger and set the variable input to an empty string. The empty string will be passed to TextArea as its value prop, and the TextArea will clear.

![Answer Clears](https://thepracticaldev.s3.amazonaws.com/i/v8f0shwnfu2m61cigvyt.gif)

####Next Post: StatsContext
In the next post we will make another `Context` to track statistics. We will also make a component to show the user the statistics.