Show Stats 
Tags: react, typescript, testing, tutorial
Published: 2020-01-17 04:47:14

We are now going to make the `Stats` component so that the user can see the stats for each card that they look at.

##User Story
* The user sees a card. They hover their mouse over an icon and a popup appears. The popup shows the user how many times they have seen the card, and how many times they have gotten the answer right or wrong.

##Features
* an `Icon` that shows up on the screen
* a `Popup` that appears when the user mouses over the `Icon`
* stats are shown to the user in the `Popup`

##Choosing Components
Now that we have the `StatsContext` we can track the stats for each card. We could put the stats on the screen all the time. But the user probably doesn't want to see them all the time. So we only want to show the stats sometimes. And instead of showing all zeros for a new question, let's make a special display that says the user hasn't seen the question before.

[Popup](https://react.semantic-ui.com/modules/popup/): We'll use a `Popup` to show the stats to the user.

[Icon](https://react.semantic-ui.com/elements/icon/): We'll show an `Icon` that the user can mouseover to trigger the Popup.

##What to test
Test that the icon shows up. Test that the popup is triggered when the user mouses over the icon. Test that the correct stats are shown in the popup.

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

Write your comments:
```javascript
//has an icon
//there's a popup
//popup appears when mouseover icon
//if there are no stats for the current question, popup tells you that you haven't seen the question before
//if there are stats for the current question, popup shows you the correct stats
```

Write your Imports at the top of the file. Notice that we are importing the initialState from CardContext, but we are renaming it to cardState. So when we refer to cardState in the tests, we are talking about the initialState object exported by CardContext.

```javascript
import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Stats from './index';
import { StatsContext } from '../../../../services/StatsContext'; 
import { StatsState } from '../../../../types';
import { CardContext } from '../../../../services/CardContext';
import { initialState as cardState } from '../../../../services/CardContext';
```

Call afterEach.
```javascript
afterEach(cleanup);
```

##Test 1: Has Icon
Write the test for the icon. We'll get the icon using a testId.
```javascript
//has an icon
it('has an icon', () => {
    // We'll get the icon by using a testId.
    const { getByTestId } = render(<Stats/>);
    const icon = getByTestId('icon')
    expect(icon).toBeInTheDocument();
});
```

![Doesn't Run](https://thepracticaldev.s3.amazonaws.com/i/azugzw8jr8wva1wyaj5c.png)

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

We'll pass the first test by rendering an [Icon](https://react.semantic-ui.com/elements/icon/) with a testId. Semantic UI React has a large set of icons that are built in. Pass the name prop to choose which one. We are using 'question circle,' which is a question mark in a circle.

Imports:
```javascript
import React, { useContext } from 'react';
import { Icon, Popup } from 'semantic-ui-react';
import { CardContext } from '../../../../services/CardContext';
import { StatsContext } from '../../../../services/StatsContext';
```

Give the icon a testId. 
```javascript
const Stats = () => <Icon data-testid='icon' name='question circle'/>    

export default Stats;
```

![Icon Passes](https://thepracticaldev.s3.amazonaws.com/i/hqaq6gmt1mrsi2qk858e.png)

##Test 2: Popup Appears
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-2.tsx)

The `Icon` is always shown on the screen. The `Popup` does not always show up on the screen. The `Popup` is triggered when the user puts their mouse cursor over the icon. So how do we simulate putting the mouse over the `Icon` to get the `Popup` to show up for our tests?

We will use `fireEvent`. We can use `fireEvent` to simulate many events, not just clicking or entering text. So let's write a test where we simulate mouseover with `fireEvent.mouseOver()`.

Make a describe block named 'theres a popup.' Inside the describe block, write the test for the `Popup`. The `Popup` will appear when the user moves their mouse over the `Icon`. 

Use `getByTestId` to get a reference to the `Icon`. Then use `fireEvent.mouseOver` to simulate the mouseover event. After firing mouseover, use `getByText` to find the textContents of the `Popup`.

```javascript
//there's a popup
describe('theres a popup', () => {
    //popup appears when mouseover icon
    it('popup exists and opens', () => {
        const { getByText, getByTestId } = render(<Stats/>);
        
        const icon = getByTestId('icon');
        expect(icon).toBeInTheDocument();
        
        //mouseOver the icon
        fireEvent.mouseOver(icon);

        const popup = getByText(/you haven't seen this question before/i);
        expect(popup).toBeInTheDocument();
    });
    
    //if there are no stats for the current question, popup tells you that you haven't seen the question before
    //if there are stats for the current question, popup shows you the correct stats
});
```
![No Popup](https://thepracticaldev.s3.amazonaws.com/i/ukgm2s82dc6mpmfmmtms.png)

Looks good, right? Yes. But I have bad news. This test *won't work* even after we add the Popup to the Stats component. The reason it will fail is because the simulated mouseOver event just doesn't work to trigger the Semantic UI React Popup component. So the popup will never show up in our test render! Let's go add the Popup to the Stats component, watch it fail, then come back and fix this test.

##Fail to Pass Test 2: Add the Popup
>File: src/scenes/Answering/components/Stats/index.tsx
>Will Match: [src/scenes/Answering/components/Stats/index-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/index-2.tsx)

Change the Stats component. Declare a const `icon` reference to the JSX call to the Icon. Instead of returning the `Icon`, return a `Popup`. The `Popup` takes a content prop. The content is the text (or anything else) that will appear inside the `Popup`. The prop 'trigger' takes the element that will appear on screen and trigger the `Popup` when the user mouses over it. Pass `icon` to the trigger prop.

```javascript
const Stats = () => {

    //declare icon as a variable
    const icon = <Icon data-testid='icon' name='question circle'/>    

return <Popup 
        content="You haven't seen this question before" 
        trigger={icon}
        />
};
```

Now save it. The popup test *should* pass. But it doesn't.
![No Popup](https://thepracticaldev.s3.amazonaws.com/i/ukgm2s82dc6mpmfmmtms.png)

The simulated mouseOver doesn't open the popup. We'll end up solving this by using fireEvent.click() to simulate a click on the Icon, which *does* trigger the popup.

##When Testing Doesn't Work How You Think It Should
To be honest, this happens a lot. You're getting used to the testing mindset, you chose your components, you know what you're trying to test, you are using commands and methods that you used before... but the test fails. Sometimes it's a typo, or you're using the wrong method. But sometimes it's just that the method you thought would work *won't work with the component you're using*. This happens a lot with components from third party libraries.

Dealing with this is just one of the many logic puzzles you work through as a programmer. The first step is to add in a call to debug() to see what is rendered. Check the documentation of each method that you're using and see if you are calling it correctly, giving it the right parameters. Try something else and see if that works. Do an internet search for your situation and check through StackOverflow, GitHub issues, Reddit, and other internet resources. Think about if you can design the test differently using a different command. 

You can get frustrated, but don't worry if it takes hours. That's just the nature of the process. Eventually you will come up with a solution that does work to test what you were doing. And if your search for an answer didn't come up with any results written by anyone else, maybe you should write a post with your solution here on dev.to!

##Pass Test 2: The Working Popup Test Using fireEvent.click()
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-3.tsx)

Here's the final, working test of the `Popup`. We have to use `fireEvent.click()` because the simulated mouseover does not trigger the `Popup` for some reason.

```javascript
//popup appears when mouseover icon
    it('popup exists and opens', () => {
        const { getByText, getByTestId } = render(<Stats/>);
        
        const icon = getByTestId('icon');
        expect(icon).toBeInTheDocument();

        //can't effectively simulate hover
        //mouseOver and mouseEnter don't trigger it
        //but click does, so... go with it
        fireEvent.click(icon);

        const popup = getByText(/you haven't seen this question before/i);
        expect(popup).toBeInTheDocument();
    });
```

![Popup Pass](https://thepracticaldev.s3.amazonaws.com/i/8mjahlu4zlhlfztvkrxv.png)

##Test 3: Popup Message for No Stats
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-4.tsx)

This test renders Stats outside of any context. When Stats doesn't see stats for the current question, it should render a popup that says "You haven't seen this question before." This test will pass when you run it. 

```javascript
    //if there are no stats for the current question, popup tells you that you haven't seen the question before
   it('without stats, you havent seen it', () => {
        const { getByText, getByTestId } = render(<Stats/>);
        const icon = getByTestId('icon');
        fireEvent.click(icon);
        const unSeen = getByText("You haven't seen this question before");
        expect(unSeen).toBeInTheDocument(); 
    });
```

![No Stats Pass](https://thepracticaldev.s3.amazonaws.com/i/w8e0ju3b5xpgtw6vq2rz.png)

That's a clue that this test is not telling us something new about the component. Let's give the `Stats` component access to `StatsContext` and `CardContext` and make sure it still passes.

##Access StatsContext and CardContext
>File: src/scenes/Answering/components/Stats/index.tsx
>Will Match: [src/scenes/Answering/components/Stats/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/index-3.tsx)

We want the `Stats` component to show the stats for the current card. To do that we need to get data from `CardContext` and `StatsContext`. `CardContext` will let us find the current card and get its question. Once we have the question we can look it up in `StatsContext`.

If there are no stats for the current card we will return a Popup that says the user hasn't seen this question before. 

Change the Stats component to this:
```javascript
const Stats = () => {
    //get cards and current index from CardContext
    const { cards, current } = useContext(CardContext);
    
    //get the current question
    const { question } = cards[current];

    //this is the entire stats context
    const allStats = useContext(StatsContext);

    //stats for the current question
    const stats = allStats[question];   

    //declare icon as a variable
    const icon = <Icon data-testid='icon' name='question circle'/>
    
    if (!stats) return (
    <Popup 
    content="You haven't seen this question before" 
    trigger={icon}
    />);


return <Popup 
        content="There are stats" 
        trigger={icon}
        />
};
```
It still passes! Good, we haven't broken anything.

##Test 4: When There Are Stats for the Current Question, Popup Shows the Stats
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-4.tsx)

Make a describe block named 'with Stats.' Make a `stats` variable, `statsState` to pass to the `StatsProvider`, and `testState` for the `CardProvider`.
 
```javascript
    describe('with Stats', () => {
        //some stats
        const stats = {
            right: 3,
            wrong: 2,
            skip: 5
        };

        //a StatsState to pass to StatsProvider
        //using the question from cards index 0
        const statsState = {
            [cardState.cards[0].question] : stats
        } as StatsState;
        
        //a CardState with current set to 0
        const testState = {
            ...cardState,
            current: 0
        };
```

Make a helper function to render `Stats` inside the `CardProvider` and `StatsProvider`. Rendering a component inside multiple providers is how you let the component access multiple contexts. This helper function will allow `Stats` to access the `CardContext` and `StatsContext` during testing.

```javascript
        //helper function to render stats inside CardProvider, StatsProvider
        const renderStats = () => render(
            <CardProvider testState={testState}>
                <StatsProvider testState={statsState}>
                    <Stats/>
                </StatsProvider>
            </CardProvider>);
```

Write the test. After we trigger the `Popup` with a simulated click event, we use getByText to look for text that says 'you have seen this question.'

```javascript
        //if there are stats for the current question, popup shows you the correct stats
        it('with stats, shows stats for that question', () => {
            const { getByText, getByTestId } = renderStats();

            const icon = getByTestId('icon');
            fireEvent.click(icon);

            const seen = getByText(/you have seen this question/i);
            expect(seen).toBeInTheDocument();        
        });
    })
```

![Popup Fail](https://thepracticaldev.s3.amazonaws.com/i/dsce23f1v2h2zvzdvuo8.png)

##Pass Test 4: When There Are Stats for the Current Question, Popup Shows the Stats
>File: src/scenes/Answering/components/Stats/index.tsx
>Will Match: [src/scenes/Answering/components/Stats/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/index-4.tsx)

Change the return values to this:

```javascript    
    if (!stats) return (
    <Popup 
    content="You haven't seen this question before" 
    trigger={icon}
    />);


return <Popup 
        content="You have seen this question" 
        trigger={icon}
        />
};
```

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

##Test 5: Popup Should Show the Total Number of Times User Has Seen the Question
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-5.tsx)

The popup should calculate the total number of times the user has seen the question. Let's test for a question they have seen 10 times.
```javascript
        it('calculates total times seen', () => {
            const { getByTestId, getByText } = renderStats();
            const icon = getByTestId('icon');
            fireEvent.click(icon);
    
            const seen = getByText(/you have seen this question/i);
            expect(seen).toBeInTheDocument();
            expect(seen).toHaveTextContent('You have seen this question 10 times.')
        });
```

![Fail to Calculate](https://thepracticaldev.s3.amazonaws.com/i/h58hqfaluy4yqyb8ywcy.png)

##Pass Test 5: Popup Should Show the Total Number of Times User Has Seen the Question
>File: src/scenes/Answering/components/Stats/index.tsx
>Will Match: [src/scenes/Answering/components/Stats/index-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/index-5.tsx)

We already get the stats for the current card in the `Stats` component. Recall that the stats are an object with three properties: right, skip, and wrong. We need to add the values of these properties together to get a total number. 

###Adding Up Total Times Seen
Use `Object.keys` to get an array of the keys from the stats for the current card. Use [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) to iterate through the keys, add the value of that key to the total, and get the total of times the user has seen it.

`Object.keys(stats)` will give us an array of three strings, `['right','skip','wrong']`.

`Array.reduce` can look more complicated than it actually is. It takes two arguments. The first argument is a function, and the second argument is the starting value. We're adding up numbers, so we'll give a starting value of 0.

Array.reduce passes two arguments to the function. The first argument is the accumulator. I named it 'acc' in this code. The first time the function runs the accumulator is the starting value. So acc will start at 0, the starting value that we passed in. Then every time the function runs the accumulator is the value that was returned by the function the last time it ran. 

The second argument is the current item in the array that is being iterated over.  I named it 'cur' in this code. The array that we are iterating over is ['right','skip','wrong']. So the first time through, `cur` will be the item at array 0, the string 'right.' We use bracket notation to look in the object stats for the value corresponding to the key 'right.' Then we add that value to the total, `acc`, and return the total. In the next iteration, the function will run with `acc` equal to the updated total, and `cur` will be the next item in the array- the string 'skip'.

##Added complexity from TypeScript 
Before we can use bracket notation and `cur` to look in `stats` and get a value, we have to cast `cur` to a key of the type of stats. Basically, we are convincing TypeScript that the variable `key` is one of the object properties of `stats`. If we tried to look at `stats[cur]`, TypeScript would throw an error even though we got the value `cur` from the array of `Object.keys` of `stats`. This is the type (haha) of thing you have to deal with fairly often when using `TypeScript`. You will be faced with a situation where you know that the code you wrote will work, but then you need to find out the right way to tell TypeScript that the code you wrote will work. It's just part of the learning curve. 

##When to Calculate the Total
Notice that we calculate the total after the first return statement. If we don't have stats, we'll return the Popup that says 'You haven't seen this question before.' If we do have stats, then we'll calculate the total before returning a Popup that reports the total.

```javascript
    if (!stats) return (
    <Popup 
    content="You haven't seen this question before" 
    trigger={icon}
    />);

    //stats is truthy, so we can calculate the total
    const total = Object.keys(stats)
    .reduce((acc, cur) => {
        //cast cur to key from the typeof stats
        //which is really the keys of Stats as defined in our src/types.ts file
        const key = cur as keyof typeof stats;
 
        //stats[key] is a number
        //set acc equal to the prior value of acc plus the value of stats[key]
        //to get the new total
        acc = acc + stats[key];

        //return the new total for the next iteration to use
        return acc;

//starting value of 0
    }, 0);
    
return <Popup
    data-testid='popup'
    content={
        <div>
            <div>You have seen this question {total} time{total !== 1 && 's'}.</div>
        </div>}
    trigger={icon}
    />
```
![Correct Total](https://thepracticaldev.s3.amazonaws.com/i/1gmtwi2vew814yb3mcx5.png)

##Test 6: Correct Value for each Stat
>File: src/scenes/Answering/components/Stats/index.test.tsx
>Will Match: [src/scenes/Answering/components/Stats/index.test-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/test-6.tsx)

Let's use test.each to test for each type of stat- 'right', 'skip', and 'wrong.' Declare `questionZero` equal to the question of the card at index 0 in cards. Declare `expectedStats` to access the stats for the question at index 0 in our stats `testState`.

Then set up the literal and the tests. We'll pass in three arguments for each test. `stat` is just a string that we use to generate the title. `regEx` is a regular expression that we will pass to getByText to find the element. `expected` is the expected number from stats. We cast the number to a string using toString() because we are comparing it to textContent, which is a string. A string will not equal a number in expect().toHaveTextContent(). 

```javascript
    //remember, current index in our testState is set to 0
        const questionZero = cardState.cards[0].question;
        const expectedStats = statsState[questionZero];

        //use test each to test for each type of stat
        test.each`
        stat        | regEx                 | expected
        ${'right'}  | ${/You got it right/i}| ${expectedStats.right.toString()}
        ${'wrong'}  | ${/Wrong/i}           | ${expectedStats.wrong.toString()}
        ${'skip'}   | ${/You skipped it/i}  | ${expectedStats.skip.toString()}
        `('Popup returns correct value of $stat, $expected', 
            ({stat, regEx, expected}) => {
                const { getByTestId, getByText } = renderStats();
                
                //open the popup
                const icon = getByTestId('icon');
                fireEvent.click(icon);

                //make find the element by regular expression
                const result = getByText(regEx);
                expect(result).toHaveTextContent(expected);
        });
```

![Popup Values Fail](https://thepracticaldev.s3.amazonaws.com/i/tsfykba893rvrnha82i2.png)

###Show the Value for Each Stat
>File: src/scenes/Answering/components/Stats/index.tsx
>Will Match: [src/scenes/Answering/components/Stats/index-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Stats/complete/index-6.tsx)

Add divs to show each stat. The total div uses the total that we calculated using Array.reduce. When total does not equal 1, we'll add 's' so it says 'times' instead of 'time.'

```javascript
    return <Popup
            data-testid='popup'
            content={
                <div>
                    <div>You have seen this question {total} time{total !== 1 && 's'}.</div>
                    <div>You got it right {stats.right}</div>
                    <div>Wrong {stats.wrong}</div>
                    <div>You skipped it {stats.skip}</div> 
                </div>}
            trigger={icon}
            />
```

![All Pass](https://thepracticaldev.s3.amazonaws.com/i/4c8nn3ovrfejptozxj85.png)

Great! All the tests pass.

#Add Stats into Answering
Now to make `Stats` available to the user, we'll add it to `Answering`.

##Decide what to test for
We don't need to re-do all the tests for `Stats` in the tests for the `Answering` component. We are already testing `Stats` in the tests for `Stats`. Let's just make sure that `Answering` has the stats `Icon`.

##Answering Test 1: Has a Stats Icon
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-16.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/test-16.tsx)

Add a new test to look for the `Icon` from the `Stats` component.
```javascript
it('has the stats icon', () => {
    const { getByTestId } = renderAnswering();
    const stats = getByTestId('icon');
    expect(stats).toBeInTheDocument();
});
```
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/redrcebcy204bjsuwy1e.png)

##Pass Answering Test 1: Has a Stats Icon
>File: src/scenes/Answering/index.tsx
>Will Match: [src/scenes/Answering/complete/index-12.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-12.tsx)

Import the Stats component. 
```javascript
import Stats from './components/Stats';
```

Change the question Header to this:
```javascript
<Header data-testid='question'><Stats/>{question}</Header>
```

The whole return value of the `Answering` component will look like this.
```javascript
    <Container data-testid='container' style={{position: 'absolute', left: 200}}>
         <Header data-testid='question'><Stats/>{question}</Header>
         <Button onClick={() => dispatch({type: CardActionTypes.next})}>Skip</Button>
         <Form>
            <TextArea data-testid='textarea'/>
        </Form>
        <Buttons answered={showAnswer} submit={() => setShowAnswer(true)}/>
        <Answer visible={showAnswer}/>
    </Container>
```
![Snapshot Fail](https://thepracticaldev.s3.amazonaws.com/i/1cgc9ow9fktjxeb5m89d.png)

Update the snapshot.

![All pass](https://thepracticaldev.s3.amazonaws.com/i/bmkwbt7oo3oj6qi3iraq.png)

Run the app. The stats Icon will show up!

![Stats Icon Shows Up](https://thepracticaldev.s3.amazonaws.com/i/x9koemwokod8hc2su1ke.gif)

#Make the stats change
We know the `Stats` component works because it passes the tests. We know the `Stats` component shows up because we test for that, too. But if you run the app you will see that the stats don't actually update when you skip or submit questions. That is because we are not dispatching any actions to `StatsContext`. So `StatsContext` doesn't receive an action and doesn't make any changes to the state. We need to dispatch an action to `StatsContext` when the user skips a question, records a right answer, or records a wrong answer. 

There are three times that we need to dispatch an action to the Stats context:
1. When the user clicks the `Skip` card button
2. When the user clicks the `Right` answer button
3. When the user clicks the `Wrong` answer button

##Answering Test 2: The Skip Button Updates Stats
>File: src/scenes/Answering/index.test.tsx
>Will Match: [src/scenes/Answering/complete/test-17.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/test-17.tsx)

Import `useContext`. We'll need it to make a helper component that displays stats.
```javascript
import React, { useContext } from 'react';
```

Import `StatsState`, `StatsContext` and `StatsProvider`.
```javascript
import { CardState, StatsState } from '../../types';
import { StatsContext, StatsProvider } from '../../services/StatsContext';
```

Add a new test above the snapshot. We'll create a `cardState`, `blankStats`, `question` and a `statsState` for this test. Then we'll make a helper component `SkipDisplay` to display the value of 'skip' for the question. We'll render `Answering` and `SkipDisplay` inside of the `CardProvider` and `StatsProvider`. Then we'll click the `Skip` button and see what happens. 

```javascript
//when the user clicks the skip button, the skip is recorded in the stats
it('clicking skip records stats', () => {
     //create a CardState with current set to 0
     const cardState = {
        ...initialState,
        current: 0
    };

    //a blank stats object
    const blankStats = {
        right: 0,
        wrong: 0,
        skip: 0
    };

    //get the question from cards index 0
    const { question } = cardState.cards[0];

    //create statsState with stats for the question
    const statsState: StatsState = {
        [question]: blankStats
    };

    //helper component displays the value of skip for the question
    const SkipDisplay = () => {
        const stats = useContext(StatsContext)
        const { skip } = stats[question];
        return <div data-testid='skipDisplay'>{skip}</div> 
    };
    
    //render Answering and SkipDisplay inside the providers
    //pass the providers the cardState and StatsState values that we defined
    const { getByTestId, getByText } = render(
        <CardProvider testState={cardState}>
            <StatsProvider testState={statsState}>
            <Answering />
            <SkipDisplay/>
        </StatsProvider>
      </CardProvider>
    );

    //find the skip button
    const skipButton = getByText(/skip/i);

    //find the skip display
    const skipDisplay = getByTestId('skipDisplay');
    
    //skip display should start at 0
    expect(skipDisplay).toHaveTextContent('0');

    //click the skip button
    fireEvent.click(skipButton);

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

![Skip Fail](https://thepracticaldev.s3.amazonaws.com/i/nc4nhorm9n9iqkflpjkf.png)

##Pass Answering Test 2: The Skip Button Updates Stats
>File: src/scenes/Answering/index.tsx
>Will Match: [src/scenes/Answering/complete/index-13.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/complete/index-13.tsx)

Import `StatsActionType`.
```javascript
//The types of action that CardContext can handle
import { CardActionTypes, StatsActionType } from '../../types';
```
Import `StatsContext`.
```javascript
import { StatsContext } from '../../services/StatsContext';
```

Use object destructuring to get the dispatch method out of `useContext(StatsContext)`. Watch out! We already have a variable called dispatch. The variable called dispatch we already have is the function that dispatches actions to the `CardContext`. So we can't call the dispatch function for the `StatsContext` 'dispatch.' We have to call the dispatch function for the `StatsContext` something else. Let's call it `statsDispatch`. 

To rename variables that you get from object destructuring you type the original variable name, a colon, and then the new name. So `const { originalName : newName } = objectToBeDestructured`. In this case, we write `dispatch: statsDispatch` to rename dispatch to statsDispatch.

```javascript
    const { dispatch: statsDispatch } = useContext(StatsContext);
```
Change the onClick function for the `Skip` button.
From
```javascript
         <Button onClick={() => dispatch({type: CardActionTypes.next})}>Skip</Button>
```
To 
```javascript
<Button onClick={() => {
            dispatch({type: CardActionTypes.next});
            statsDispatch({type: StatsActionType.skip, question});   
         }}>Skip</Button>
```
Notice that the anonymous function now contains two expressions. Because there is more than one expression, we have to enclose the expressions in curly brackets. We switch from a concise function body without brackets, to a block body with brackets.

![Skip Pass](https://thepracticaldev.s3.amazonaws.com/i/xxnxna3vcvus1e863sof.png)

Run your app and click the `Skip` button twice. Clicking it twice will get you back to the first question. Mouse over the stats icon. The stats popup will now show correct totals for each question.

![Popup With Stats](https://thepracticaldev.s3.amazonaws.com/i/6bmfwykiw01ncoqjz8lp.gif)

#Right And Wrong Buttons
Now let's make the `Right` and `Wrong` buttons update `StatsContext`.

##What to Test
* Clicking the `Right` button updates stats
* Clicking the `Wrong` button updates stats

We'll use the same techniques that we used to test the `Skip` Button. We'll make a helper component `StatsDisplay` to show the stats, render `Buttons` and `StatsDisplay` inside of Providers, and check `StatsDisplay` to make sure `Buttons` successfully dispatches actions.

##Buttons Test 1: the Right Button Updates Stats
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-7.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-7.tsx)

Import `StatsState`.
```javascript
import { CardState, StatsState } from '../../../../types';
```

Import `StatsContext` and `StatsProvider`.
```javascript
import { StatsContext, StatsProvider } from '../../../../services/StatsContext';
```

Make a describe block named 'clicking buttons records stats.' Declare `cardState`, `blankStats`, and the `question` from the card at index 0. Make a `StatsDisplay` helper component to display right and wrong from the `StatsContext`. 

Make a `renderWithDisplay` helper function to render `Buttons` and `StatsDisplay` inside the `CardProvider` and `StatsProvider` with the `cardState` and `statsState`.

```javascript
//when the user clicks the skip button, the skip is recorded in the stats
describe('clicking buttons records stats', () => {
    //create a CardState with current set to 0
    const cardState = {
       ...initialState,
       current: 0
   };

   //a blank stats object
   const blankStats = {
       right: 0,
       wrong: 0,
       skip: 0
   };

   //get the question from cards index 0
   const { question } = cardState.cards[0];

   //create statsState with stats for the question
   const statsState: StatsState = {
       [question]: blankStats
   };

   //helper component displays the value of skip for the question
   const StatsDisplay = () => {
       const stats = useContext(StatsContext)
       const { right, wrong } = stats[question];
       return <div>
           <div data-testid='rightDisplay'>{right}</div>
           <div data-testid='wrongDisplay'>{wrong}</div>
           </div> 
   };
   
   const renderWithDisplay = () => render(
    <CardProvider testState={cardState}>
        <StatsProvider testState={statsState}>
        <Buttons answered={true} submit={jest.fn()} />
        <StatsDisplay/>
    </StatsProvider>
  </CardProvider>
);

//clicking the right button updates stats

//clicking the wrong button updates stats

});
```

Write the test for the right button inside the describe block.
```javascript
//clicking the right button updates stats
it('clicking the right button updates stats', () => {
        //render Answering and StatsDisplay inside the providers
        //pass the providers the cardState and StatsState values that we defined
        const { getByTestId, getByText } = renderWithDisplay();

        //find the right button
        const rightButton = getByText(/right/i);

        //find the right display
        const rightDisplay = getByTestId('rightDisplay');
        
        //right display should start at 0
        expect(rightDisplay).toHaveTextContent('0');

        //click the right button
        fireEvent.click(rightButton);

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

![Right Button Not Update Stats](https://thepracticaldev.s3.amazonaws.com/i/jbk9adxn8oczzg9t07qx.png)

##Pass Buttons Test 1: the Right Button Updates Stats
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/scenes/Answering/components/Buttons/index-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-6.tsx)

Import `StatsActionType`.
```javascript
import { CardActionTypes, StatsActionType } from '../../../../types';
```

Import `StatsContext`.
```javascript
import { StatsContext } from '../../../../services/StatsContext';
```

Change the `Buttons` component. Get cards and current from `CardContext` so that you can then get the question from the current card. Get dispatch from `StatsContext` and rename it to `statsDispatch` so it won't conflict with the `CardContext` dispatch. Change the onClick function for the `Right` button to `statsDispatch` an action with a type `StatActionType.right`.

```javascript
const Buttons = ({
    answered,
    submit
}:{
    answered: boolean,
    submit: () => void
}) => {
    //get cards and current so that we can get the question
    const { cards, current, dispatch } = useContext(CardContext);
    //get the question so we can track stats
    const { question } = cards[current];

    //to dispatch actions to the StatsContext
    const { dispatch: statsDispatch } = useContext(StatsContext);

    return answered
    ?   <Button.Group>
            <Button content='Right' positive 
                onClick={() => {
                    statsDispatch({ type: StatsActionType.right, question })
                    dispatch({ type: CardActionTypes.next })
                }}/>
            <Button.Or/>
            <Button content='Wrong' negative 
                onClick={() => dispatch({ type: CardActionTypes.next })}
            />    
        </Button.Group>
    :   <Button content='Submit' onClick={() => submit()}/>
};
```
![Right Button Pass](https://thepracticaldev.s3.amazonaws.com/i/40j2exvjohzr4vvrkuat.png)

##Buttons Test 2: the Wrong Button Updates Stats
>File: src/scenes/Answering/components/Buttons/index.test.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/test-8.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/test-8.tsx)

Add the test inside the describe block.
```javascript
    //clicking the wrong button updates Stats
    it('clicking the wrong button updates stats', () => {
        //render Answering and StatsDisplay inside the providers
        //pass the providers the cardState and StatsState values that we defined
        const { getByTestId, getByText } = renderWithDisplay();

        //find the wrong button
        const wrongButton = getByText(/wrong/i);

        //find the wrong display
        const wrongDisplay = getByTestId('wrongDisplay');
        
        //wrong display should start at 0
        expect(wrongDisplay).toHaveTextContent('0');

        //click the wrong button
        fireEvent.click(wrongButton);

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

##Pass Buttons Test 2: the Wrong Button Updates Stats
>File: src/scenes/Answering/components/Buttons/index.tsx
>Will Match: [src/scenes/Answering/components/Buttons/complete/index-7.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Answering/components/Buttons/complete/index-7.tsx)

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

Load up the app and try out the buttons. You'll see the stats in the popup update correctly.

![Stats](https://thepracticaldev.s3.amazonaws.com/i/8u78xqyvdq2fai2wx3e5.png)