StatsContext
Tags: react, typescript, testing, tutorial
Published: 2020-01-17 04:46:14

In this post we will make the Context that will track the stats (short for statistics) for each question. This Context will be called `StatsContext`. `StatsContext` will track how many times the user has answered each question right, wrong, and how many times the user has skipped that question.

In the next post we will make a `Stats` component. The `Stats` component will show the stats to the user. The `Stats` component will appear on the `Answering` screen.

##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
* Stats for cards are tracked
* `Right`, `Wrong`, and `Skip` buttons update `StatsContext`
* User can see the stats for the card they are looking at

To make these features work we will
* Define the types for Stats
* Make the `StatsContext`
* Write the tests for the `Stats` Component
* Make the `Stats` component
* Change the tests for `Answering`
* Add the `Stats` component to **Answering**

##Add Stats Types to Types.ts
>File: src/types.ts
>Will match: [src/complete/types-4.ts](https://github.com/jacobwicks/flashcard/blob/master/src/complete/types-4.ts)

Add the interface `Stats` to types. `Stats` describes the stats for a single question.

```javascript
//The stats for a single question
export interface Stats {

    //number of times user has gotten it right
    right: number,
    
    //number of times user has gotten it wrong
    wrong: number,
    
    //number of times user has seen the question but skipped it instead of answering it
    skip: number
};
```
Add the interface `StatsType`. StatsType is an object with a a string for an [index signature](https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks). Putting the index signature in `StatsType` means that TypeScript will expect that any key that is a string will have a value that is a `Stats` object.

We will use the question from `Cards` as the key to store and retrieve the stats.
 
```javascript
//an interface with an string index signature
//each string is expected to return an object that fits the Stats interface
//the string that we will use for a signature is the question from a Card object
export interface StatsType {
    [key: string]: Stats
};
```

Describe the `StatsDispatch` function and the `StatsState` type.

###StatsDispatch
To change the contents of `StatsContext` we will have our components dispatch actions to `StatsContext`. This works just like dispatching actions to the `CardContext`. To dispatch actions to `StatsContext` we will use `useContext` to get dispatch out of `StatsContext` inside components that use `StatsContext`. `StatsContext` contains `StatsState`. We have to tell TypeScript that the key 'dispatch' inside `StatsState` will contain a function. 

###StatsState
`StatsState` is a [union type](https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types). A union type is a way to tell TypeScript that a value is going to be one of the types in the union type.
 
StatsState puts together `StatsType` and `StatsDispatch`. This means that TypeScript will expect a `Stats` object for every key that is a string in `StatsState`, except for 'dispatch,' where TypeScript will expect the `dispatch` function.

```javascript
//The StatsDispatch function
interface StatsDispatch {
    dispatch: (action: StatsAction) => void
};

//a union type. The stats state will have a Stats object for any given key
//except dispatch will return the StatsDispatch function
export type StatsState = StatsType & StatsDispatch
```

###StatsActionType and StatsAction
The enum `StatsActionType` and the type `StatsAction` define the types of actions that we can dispatch to `StatsContext`. Later in this post you will write a case for each type of `StatsAction` so the reducer in `StatsContext` can handle it. In addition to the type, each action takes a parameter called 'question.' The 'question' is a string, same as the question from the `Card` objects. When the reducer receives an action, it will use the question as the key to find and store the stats.

```javascript
//an enum listing the three types of StatsAction
//A user can get a question right, wrong, or skip it
export enum StatsActionType {
    right = 'right',
    skip = 'skip',
    wrong = 'wrong'
};

//Stats Action
//takes the question from a card 
export type StatsAction = { 
    type: StatsActionType, 
    question: string 
};
```

#Create `StatsContext`

##Testing `StatsContext`
Our tests for `StatsContext` will follow the same format as the tests we wrote for `CardContext`. We will test the `Provider`, the `Context`, and the `reducer`. We will start by testing the `reducer` to make sure that it handles actions correctly and returns the state that we expect. We'll test that the `Provider` renders without crashing. Then we will write a helper component to make sure that the `Context` returns the right data. 

Recall that the `reducer` is what handles actions and makes changes to the state held in a Context. The `reducer` will add new stats objects when it sees a question that isn't being tracked yet. The `reducer` will add to the stats numbers for a question when it receives an action. 

##Choosing What to Test
* `reducer` returns state
* `reducer` adds a new stats object when it receives a new question
* `reducer` handles right action, returns correct stats
* `reducer` handles skip action, returns correct stats
* `reducer` handles wrong action, returns correct stats
* `StatsContext` provides an object with Stats for questions

We'll start testing with the reducer.

##Test 1: Reducer Takes State, Action and returns State 
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-1.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-1.tsx)

Write a comment for each test we are going to make.

```javascript
//reducer
    //returns state
    //adds a new stats object when it receives a new question
    //handles right action, returns correct stats
    //handles skip action, returns correct stats
    //handles wrong action, returns correct stats

//StatsContext provides an object with Stats for questions
```

The `reducer` takes a state object and an action object and returns a new state object. When the action type is undefined, the reducer should return the same state object that it received.

Imports and the first test. Declare state, an empty object. Declare action as an object with an undefined type. 

```javascript
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { reducer } from './index';

afterEach(cleanup);

describe('StatsContext reducer', () => {
    it('returns state', () => {
        const state = {};
        const action = { type: undefined };
        expect(reducer(state, action)).toEqual(state);
    });
});
```

![Suite Failed To Run](https://thepracticaldev.s3.amazonaws.com/i/mx32ju5d87kp0ftfsueo.png)

##Passing Test 1: Reducer Takes State, Action and returns State 
>File: src/services/StatsContext/index.tsx
>Will Match: [src/services/StatsContext/complete/index-1.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/index-1.tsx)

Write the first version of the `reducer`. Remember that the `reducer` takes two parameters. 

The first parameter is the state object. The state object type is `StatsState`. 

The second parameter is the action object. The action object type is `StatsAction`. 

Imports:
```javascript
import { StatsAction, StatsState } from '../../types';
```

Write the `reducer`:

```javascript
//the reducer handles actions
export const reducer = (state: StatsState, action: StatsAction) => {
    //switch statement looks at the action type
    //if there is a case that matches the type it will run that code
    //otherwise it will run the default case
    switch(action.type) {
        //default case returns the previous state without changing it
        default: 
            return state
    }
};
```

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

##Test 2 Preparation: Add `blankStats` and `initialState` to StatsContext file
>File: src/services/StatsContext/index.tsx
>Will Match: [src/services/StatsContext/complete/index-2.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/index-2.tsx)

Before we write the tests, we need to add the `blankStats` and `initialState` objects to the `StatsContext` file.
 
Imports the types.
```javascript
import { Stats, StatsAction, StatsState } from '../../types';
```

Create the `blankStats` object. Later, the `reducer` will copy this object to create the `Stats` object used to track new questions. Put `blankStats` in the file above the `reducer`.
 
```javascript
//a Stats object
//use as the basis for tracking stats for a new question
export const blankStats = {
    right: 0,
    wrong: 0,
    skip: 0
} as Stats;
```

Create the `initialState`. Put it after the `reducer`.
```javascript
//the object that we use to make the first Context
export const initialState = {
    dispatch: (action: StatsAction) => undefined
} as StatsState;
```

Ok, now we are ready to write the second test.

##Test 2: `reducer` Adds a New `Stats` Object When it Receives a New Question
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-2.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-2.tsx)

The next test we are going to write is 'adds a new stats object when it receives a new question.' That's a good thing to test. But shouldn't we test each case to make sure it works? Will we have to write three tests?

And what about all the tests after that?
* handles `right` action, returns correct stats
* handles `skip` action, returns correct stats
* handles `wrong` action, returns correct stats

Those are probably going to be basically the same test. Do we really have to write the same code three times? No, we don't! Jest provides a way to make and run tests from a list of arguments. The way to make and run multiple tests from a list of arguments is the `it.each` method.  

First we'll write a single test to show that the `right` case in the `reducer` adds a new stats object to the state. Then we'll write the code to pass that test. After that, I'll show you how to use `it.each` to make many tests at once when you want to test a lot of things with similar code. We'll replace the individual test with code that generates three tests, one to test each case.

##Make the Single Test for `reducer` Handles `right` Action

Import the `blankStats` and `initialState` from `StatsContext`. Import `StatsActionType` from types.
 
```javascript
import { blankStats, initialState, reducer } from './index';
import { StatsActionType } from '../../types';
```

Write the test. 
```javascript
    //adds a new stats object when it receives a new question
    it('adds a new stats object when it receives a new question', () => {
        const question = 'Example Question';
        
        //the action we will dispatch to the reducer
        const action = {
            type: StatsActionType.right,
            question
        };

        //the stats should be the blankStats object
        //with right === 1
        const rightStats = {
            ...blankStats,
            right: 1
        };

        //check to make sure that initialState doesn't already have a property [question]
        expect(initialState[question]).toBeUndefined();

        const result = reducer(initialState, action);

        //after getting a new question prompt in an action type 'right'
        //the question stats should be rightStats
        expect(result[question]).toEqual(rightStats);
    });
```
That looks pretty similar to the tests we've written before. 
![Fails](https://thepracticaldev.s3.amazonaws.com/i/yn204qajghd3xen34kdx.png)

Run it, and it will fail.

##Pass the Single Test for `reducer` Handles `right` Action

Now let's write the code for the `reducer` to handle actions with the type 'right.' 
The case will need to:
* Get the question out of the action. 

* Get the previous stats. To find the previous stats, first look in the state for a property corresponding to the question. If there are stats for the question already, use those. Otherwise, use the **blankStats** object.

* Make the new stats. Use the previous stats, but increment the target property by one. e.g. right: prevStats.right + 1.

* Make a new state object. Assign newStats as the value of the question.

* Return the new state.

Remember, the cases go inside the switch statement. Add case 'right' to the switch statement in the `reducer` and save it.

```javascript
        case 'right': {
            //get the question from the action
            const { question } = action;

            //if the question is already in state, use those for the stats
            //otherwise, use blankStats object
            const prevStats = state[question] ? state[question] : blankStats;
            
            //create newStats from the prevStats
            const newStats = {
                ...prevStats,
                //right increases by 1
                right: prevStats.right + 1
            };
  
            //assign newStats to question
            const newState = {
                ...state,
                [question]: newStats
            };
  
            return newState;
        }
```

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

##Case `right`, `wrong` and `skip` Will All Be Basically the Same Code
If you understand how the code for case `right` works, think about how you would write the code for the other cases, `wrong` and `skip`. It's pretty much the same, isn't it? You'll just be targeting different properties. `wrong` instead of `right`, etc.

##What Will the Tests Look Like?
The tests will look very repetitive. In fact, the tests would be the same. To test `wrong`, you would copy the test for `right` and just replace the word 'right' with the word 'wrong.' Writing all these tests out would be a waste of time when we will have three cases that all work the same. Imagine if you had even more cases that all worked the same! Or if you wanted to test them with more than one question prompt. You would be doing a lot of copying and pasting.

Jest includes a way to generate and run multiple tests. The `it.each()` method. 
Delete the test we just wrote for 'adds a new stats object when it receives a new question.' We don't need it anymore. We are going to replace it with code that generates and runs multiple tests.

#Tests: Using it.Each to Generate Multiple Tests
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-3.tsx)

[it.each()](https://jestjs.io/docs/en/api.html#testeachtablename-fn-timeout) is the method that generates and runs multiple tests. Because `it()` is an alias for `test()`, you can also use `test.each()` if you think that sounds better. We'll start out using `it.each()` in this post, but later in the tutorial we'll use `test.each()` when we run multiple tests.

The API, which means the arguments that `it.each()` accepts and the way you use them, are different from what you would expect. One thing to note is that the code that you write to generate the title for each test uses a weird format called [printf formatting](https://nodejs.org/api/util.html#util_util_format_format_args). That's why you'll see % signs in the titles when we write them.

To make it.each work we will
* Use [Object.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values) to get an array containing each value in the enum StatsActionType
* Use [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) to iterate over the StatsActionType array
* for each StatsActionType we will make an array of arguments that it.each will turn into a test
* So we'll end up with an array of arrays of test arguments
* We'll pass that array to it.each(). it.each() will print a test name based on the arguments and then run a test using the arguments

Start by making a describe block.
```javascript
    describe('Test each case', () => {

});
```
##Inside the describe block 'Test each case'
Write the functions that we'll use to generate the arguments for it.each().

Make a helper function that takes a StatsActionType and returns a Stats object with the argument type set to 1. 

```javascript
const getStats = (type: StatsActionType) => ({...blankStats, [type]: 1});
```

[Bracket Notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors) doesn't mean there's an array. Bracket notation is a way of accessing an object property using the value of the variable inside the brackets. So when you call getStats('right') you will get back an object made by spreading blankStats and setting right to 1.

The `getStats` returns an object. It has a [Concise Body and an Implicit Return](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). Surrounding the return value in parentheses is a way of telling the compiler that you are returning an object. The curly brackets enclose the object that is getting returned. Without the parentheses around them, the compiler would read the curly brackets as the body of the function instead of a returned value.

Declare an example question.
```javascript
const exampleQuestion = 'Is this an example question?';
```
Make a helper function that accepts a StatsActionType and returns a StatAction object.
```javascript
        //function that takes a StatsActionType and returns an action
        const getAction = (
            type: StatsActionType, 
            ) => ({
                type,
                question: exampleQuestion
        });
```

Inside the first describe block make another describe block. This is called 'nesting' describe blocks. Nested describe blocks will print out on the test screen inside of their parent blocks. Also, variables that are in scope for outer describe blocks will be available to inner describe blocks. So we can use all the variables we just declared in any test that is inside the outer describe block.

```javascript
describe('Reducer adds a new stats object when it receives a new question prompt', () => {

});
```

##Inside the Describe Block 'Reducer adds a new stats object when it receives a new question prompt'

Write the code to generate the arguments that we will pass to it.each.
`Object.values` will give us an array of each value in StatsActionType: `['right', 'skip', 'wrong']`.

`Array.map` will iterate through each value in that array and return a new array.
In the callback function we pass to `map` we'll create an action object, the results that we expect to see, and return the array of arguments for the test.
 
```javascript
 //uses Array.map to take each value of the enum StatsActionType
            //and return an array of arguments that it.each will run in tests
            const eachTest = Object.values(StatsActionType)
            .map(actionType => {
                //an object of type StatAction
                const action = getAction(actionType);

                //an object of type Stats
                const result = getStats(actionType);

                //return an array of arguments that it.each will turn into a test
                return [
                    actionType,
                    action,
                    initialState,
                    exampleQuestion,
                    result
                ];
            });
```

Use `it.each` to run all the tests. Each test will get an array of five arguments. If we wanted to rename the arguments, we could, but to try and make it easier to read we will name the arguments the same thing that we named them when we created them.

I'm not going to explain printf syntax, but [here's a link](https://nodejs.org/api/util.html#util_util_format_format_args) if you're curious.
```javascript
            //pass the array eachTest to it.each to run tests using arguments
            it.each(eachTest)
            //printing the title from it.each uses 'printf syntax'
            ('%#: %s adds new stats', 
            //name the arguments, same order as in the array we generated
            (actionType, action, initialState, question, result) => {
                    //assert that question isn't already in state
                    expect(initialState[question]).toBeUndefined();

                    //assert that the stats object at key: question matches result
                    expect(reducer(initialState, action)[question]).toEqual(result);
            });
```
![Fails 2 auto tests](https://thepracticaldev.s3.amazonaws.com/i/wpuay89v5bgi5z4ygg55.png)

##Pass the `it.each` Tests for `skip` and `wrong`
>File: src/services/StatsContext/index.tsx
>Will Match: [src/services/StatsContext/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/index-3.tsx)

Write the case for `skip` and add it to the switch statement. Notice that we use bracket notation and the ternary operator to get the value for `prevStats`.

```javascript
        //user skipped a card
        case 'skip': {
            //get the question from the action
            const { question } = action;

            //if the question is already in state, use those for the stats
            //otherwise, use blankStats object
            const prevStats = state[question] ? state[question] : blankStats;
            
            //create newStats from the prevStats
            const newStats = {
                ...prevStats,
                //skip increases by 1
                skip: prevStats.skip + 1
            };
  
            //assign newStats to question
            const newState = {
                ...state,
                [question]: newStats
            };
  
            return newState;
        }
```
![Passes two of the auto tests](https://thepracticaldev.s3.amazonaws.com/i/vekujw1rp4lklmk8hwez.png)

##How Would You Write the Code for Case `wrong`?
>File: src/services/StatsContext/index.tsx
>Will Match: [src/services/StatsContext/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/index-3.tsx)

Try writing the case to handle `wrong` actions on your own before you look at the example below. Hint: Look at the cases `right` and `skip`.

```javascript
        //user got a question wrong
        case 'wrong': {
            //get the question from the action
            const { question } = action;

            //if the question is already in state, use those for the stats
            //otherwise, use blankStats object
            const prevStats = state[question] ? state[question] : blankStats;
            
            //create newStats from the prevStats
            const newStats = {
                ...prevStats,
                //wrong increases by 1
                wrong: prevStats.wrong + 1
            };
    
            //assign newStats to question
            const newState = {
                ...state,
                [question]: newStats
            };
    
            return newState;
        }
```
![Passes all Auto Tests So far](https://thepracticaldev.s3.amazonaws.com/i/nduki4oqizql68mr4g40.png)

##Test 4: Results for Existing Questions
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-4.tsx)

Rewrite the helper function `getStats()` to take an optional parameter `stats`, a Stats object. The '?' tells TypeScript that the parameter is optional. If `getStats` receives `stats`, create the new Stats object by spreading the argument received for `stats`. Otherwise, spread the imported `blankStats` object.

```javascript
//function that takes a StatsActionType and returns a Stats object
        //may optionally take a stats object
        const getStats = (
            type: StatsActionType, 
            stats?: Stats
            ) => stats 
                    ? ({ ...stats,
                        [type]: stats[type] + 1 })
                    : ({ ...blankStats,
                        [type]: 1 });
```

Create a new describe block below the describe block 'Reducer adds a new stats object when it receives a new question prompt' but still nested inside the describe block 'Test each case.'

Name the new describe block 'Reducer returns correct stats.'
```javascript
        describe('Reducer returns correct stats', () => {
})
```
##Inside the describe block 'Reducer returns correct stats'

Write a StatsState object, `existingState`.
```javascript
        //create a state with existing questions
        const existingState = {
            ...initialState,
            [examplePrompt]: {
                right: 3,
                skip: 2,
                wrong: 0
            },
            'Would you like another example?': {
                right: 2,
                skip: 0,
                wrong: 7
            }
        };
```

Use Object.values and Array.map to create the test arguments.
```javascript
        //Object.Values and array.map to turn StatsActionType into array of arrays of test arguments
        const existingTests = Object.values(StatsActionType)
        .map(actionType => {
            //get the action with the type and the example prompt
            const action = getAction(actionType);
            
            //get the stats for examplePrompt from existingState
            const stats = existingState[exampleQuestion];

            //getStats gives us our expected result
            const result = getStats(actionType, stats);

            //return the array
            return [
                actionType,
                action,
                existingState,
                result,
                exampleQuestion,
            ];
        });

```

Use it.each to run the array of arrays of test arguments.
```javascript
  it.each(existingTests)
        ('%#: %s returns correct stats',
            (actionType, action, initialState, result, question) => {
                //assert that question is already in state
                expect(initialState[question]).toEqual(existingState[exampleQuestion]);
                //assert that the stats object at key: question matches result
                expect(reducer(initialState, action)[question]).toEqual(result);
        });
```
![Passes Auto Tests](https://thepracticaldev.s3.amazonaws.com/i/akt72aotj2lm14x3jmrr.png)

That's it! Now you know one way to generate multiple tests. There are other ways to generate multiple tests. [it.each()](https://jestjs.io/docs/en/api.html#testeachtablename-fn-timeout) can take a [template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) instead of an array of arrays. We'll make multiple tests that way later. There is also a separate library you can install and use called [jest in case](https://github.com/atlassian/jest-in-case).

####Tests That Pass When You Write Them
These tests all pass because we already wrote the code to pass them. If a test passes when you write it, you should always be at least a little suspicious that the test isn't telling you anything useful. Can you make the tests fail by changing the tested code? Try going into the index file and changing the code for one of the cases in the reducer's switch statement so it doesn't work. Does the test fail? If it still passes, then that's bad!

##Test 5: `StatsProvider` Renders Without Crashing
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-5.tsx)

Add an import of the `StatsProvider` from `StatsContext`. We will write the `StatsProvider` to pass this test.

```javascript
import { blankStats, initialState, reducer, StatsProvider } from './index';
```

Make a describe block named 'StatsProvider.'
Write the test to show that the StatsProvider renders without crashing. Recall from testing `CardContext` that the React Context Provider component requires a prop `children` that is an array of components. That's why we render `StatsProvider` with an array of children. If you prefer, you can use JSX to put a child component in `StatsProvider` instead of passing the array.

```javascript
//StatsContext provides an object with Stats for questions
describe('StatsProvider', () => {
    it('renders without crashing', () => {
        render(<StatsProvider children={[<div key='child'/>]}/>)
    });
})
```
This test will fail because we haven't written the `StatsProvider` yet.

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

##Pass Test 5: `StatsProvider` Renders Without Crashing
>File: src/services/StatsContext/index.tsx
>Will Match: [src/services/StatsContext/complete/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/index-4.tsx)

We'll use `createContext` and `useReducer` to make the `StatsContext` work. Import them from React.

```javascript
import React, { createContext, useReducer } from 'react';
```

Declare the `initialState`. We'll put a placeholder `dispatch` function in there.  We just have to have it to stop TypeScript from throwing an error. This placeholder makes our `initialState` object fit the `StatsState` union type that we declared. The placeholder `dispatch` accepts the correct type of argument, the `StatsAction`. But the placeholder will be replaced with the actual dispatch `function` inside the `CardProvider`.

```javascript
//the object that we use to make the first Context
export const initialState = {
    dispatch: (action: StatsAction) => undefined
} as StatsState;
```

Use `createContext` to create the `StatsContext` from the `initialState`.
```javascript
const StatsContext = createContext(initialState);
```

Declare the props for the `StatsProvider`. `StatsProvider` can accept ReactNode as its children. We can also declare the optional prop `testState`, which is a StatsState. When we want to override the default `initialState` for testing purposes we just need to pass a `testState` prop to `StatsProvider`.
 
```javascript
//the Props that the StatsProvider will accept
type StatsProviderProps = {
    //You can put react components inside of the Provider component
    children: React.ReactNode;
    
    //We might want to pass a state into the StatsProvider for testing purposes
    testState?: StatsState
};
```

Write the `StatsProvider` and the exports. If you want to review the parts of the `Provider`, take a look at the `CardProvider` in post 6, where we made `CardContext`.

We use Array Destructuring to get the state object and the dispatch function from useReducer. We return the `Provider` with a value prop created by spreading the state and the reducer. This is the actual reducer function, not the placeholder that we created earlier. Child components are rendered inside the `Provider`. All child components of the `Provider` will be able to use `useContext` to access the `StatsContext`.

```javascript
const StatsProvider = ({ children, testState }: StatsProviderProps) => {
    const [state, dispatch] = useReducer(reducer, testState ? testState : initialState);
    const value = {...state, dispatch} as StatsState;
    return (
        <StatsContext.Provider value={value}>
            {children}
        </StatsContext.Provider>
    )};

export { 
    StatsContext, 
    StatsProvider 
};
```
Great! Now the StatsProvider renders without crashing. 

![Renders Without Crashing](https://thepracticaldev.s3.amazonaws.com/i/v8i94fr43qfwin1zeoxi.png)

#Test 6: Does Stats Context Provide Stats Values
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-6.tsx)

To test if the `StatsProvider` is providing the correct values for `StatsContext`, we are going to write a helper component. Let's list the features we are trying to test:

##Features
* provides value for right
* provides value for skip
* provides value for wrong

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

Inside the 'StatsProvider' describe block, make the helper component `StatsConsumer`. `StatsConsumer` uses `useContext` to access `StatsContext`, and will display the stats that it receives. Rendering `StatsConsumer` will allow us to check if `StatsContext` and `StatsProvider` are working correctly.

```javascript
    //A helper component to get Stats out of StatsContext
    //and display them so we can test
    const StatsConsumer = () => {
        const stats = useContext(StatsContext);
       
        //stats is the whole StatsState
        //one of its keys is the dispatch key, 
        //so if there's only 1 key there's no stats
        if (Object.keys(stats).length < 2) return <div>No Stats</div>;

        //use the filter method to grab the first question
        const question = Object.keys(stats).filter(key => key !== 'dispatch')[0];
        const { right, skip, wrong } = stats[question];

        //display each property in a div
        return <div>
            <div data-testid='question'>{question}</div>
            <div data-testid='right'>{right}</div>
            <div data-testid='skip'>{skip}</div>
            <div data-testid='wrong'>{wrong}</div>
        </div>
    };
```

Create `exampleQuestion` and `testState`. You can copy and paste the `existingState` from inside the 'reducer' describe block above.

```javascript
    const exampleQuestion = 'Is this an example question?';
    
    //create a state with existing questions
    const testState: StatsState = {
        ...initialState,
        [exampleQuestion]: {
            right: 3,
            skip: 2,
            wrong: 0
        },
        'Would you like another example?': {
            right: 2,
            skip: 0,
            wrong: 7
        }
    };
```

Make a nested describe block 'StatsContext provides stats object.' Make a helper function `renderConsumer` to render `StatsConsumer` inside the `StatsProvider`. Pass `StatsProvider` the `testState` object.

Test `question`, `right`, `skip`, and `wrong`.

```javascript
 //StatsContext returns a stats object
    describe('StatsContext provides stats object', () => {
        const renderConsumer = () => render(
            <StatsProvider testState={testState}>
                <StatsConsumer/>
            </StatsProvider>)
        
        it('StatsConsumer sees correct question', () => {
            const { getByTestId } = renderConsumer();
            const question = getByTestId('question');
            expect(question).toHaveTextContent(exampleQuestion);
        })

        it('StatsConsumer sees correct value of right', () => {
            const { getByTestId } = renderConsumer();
            const right = getByTestId('right');
            expect(right).toHaveTextContent(testState[exampleQuestion].right.toString());
            })

        it('StatsConsumer sees correct value of skip', () => {
            const { getByTestId } = renderConsumer();
            const skip = getByTestId('skip');
            expect(skip).toHaveTextContent(testState[exampleQuestion].skip.toString());
            })

        it('StatsConsumer sees correct value of wrong', () => {
            const { getByTestId } = renderConsumer();
            const wrong = getByTestId('wrong');
            expect(wrong).toHaveTextContent(testState[exampleQuestion].wrong.toString());    
        })
    })
```
![StatsConsumer passes](https://thepracticaldev.s3.amazonaws.com/i/oj76elsvhr2a0t83difg.png)


##Test 7: it.each() With Tagged Literal
>File: src/services/StatsContext/index.test.tsx
>Will Match: [src/services/StatsContext/complete/test-7.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/services/StatsContext/complete/test-7.tsx)

`it.each()` can take an array of arrays. `it.each` can also accept a tagged literal. A tagged literal, or [template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), sounds way more complicated than it is. A tagged literal is information inside of backticks. They are pretty common in modern javascript, and very useful.

To use a tagged literal for your `it.each` tests, you basically write out a table   and let it.each run through the table. You declare the names of your arguments in the top row, and separate everything with the pipe | character.

Delete the three tests that we wrote for the value of `right`, `skip`, and `wrong`. Replace them with this example of it.each using a tagged literal.

This example also calls `it` by its alternate name, `test`. Remember, the 'it' method is an alias for the 'test' method. So calling test.each is the same as calling it.each. I think "test each" sounds better than "it each," so I usually use test.each when I'm running multiple tests.

```javascript
        it('StatsConsumer sees correct question', () => {
            const { getByTestId } = renderConsumer();
            const question = getByTestId('question');
            expect(question).toHaveTextContent(exampleQuestion);
        });

        test.each`
        type        | expected
        ${'right'}  | ${testState[exampleQuestion].right.toString()}
        ${'skip'}   | ${testState[exampleQuestion].skip.toString()}
        ${'wrong'}  | ${testState[exampleQuestion].wrong.toString()}
        `('StatsConsumer sees correct value of $type, returns $expected', 
            ({type, expected}) => {
                const { getByTestId } = renderConsumer();
                const result = getByTestId(type);
                expect(result).toHaveTextContent(expected);
        });
```
See how in the top row we named our arguments? The first column is named 'type' and the second column is named 'expected.' Also notice that when we are printing the title we can refer to them by name instead of using the printf format. Like I said earlier, the test.each API is different from how you'd expect it to be. 

We use object destructuring to get type and expected out of the arguments passed to each test. Then writing the tests goes as normal.  

![StatsConsumer passes generated tests](https://thepracticaldev.s3.amazonaws.com/i/js3nr6mc2dklr6sfj7cn.png)

If you have a few minutes, try adding another column to the arguments. Try renaming the arguments. Try changing the titles of the tests, and rewriting the matchers and assertions. 

Ok, now we have confidence that the `StatsProvider` is working. Let's import the `StatsProvider` into the App, then make the `Stats` component that will show `Stats` to the user.

##Import StatsProvider into the App
>File: src/App.tsx
>Will Match: [src/complete/app-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/app-4.tsx)

We've got the StatsContext written. Now let's make the stats from StatsContext available to the components. You will make StatsContext available by importing the StatsProvider into the App and wrapping the components in the StatsProvider.

Go to /src/App.tsx. Change it to this:
```javascript
import React from 'react';
import './App.css';
import Answering from './scenes/Answering';
import { CardProvider } from './services/CardContext';
import { StatsProvider } from './services/StatsContext';

const App: React.FC = () => 
    <CardProvider>
      <StatsProvider>
        <Answering />
      </StatsProvider>
    </CardProvider>

export default App;
```

Great! Now the contents of the stats context will be available to the Answering component. It will also be available to any other components that you put inside the `StatsProvider`.

##Try Refactoring
Look at the code for the `StatsContext` `reducer`. Cases `right`, `skip`, and `wrong` have almost the same code inside of them. They each get the previous stats the same way. They each create the `nextStats` object and the `nextState` object the same way.

Can you write a single function `getPrevStats` that each case can call to get the previous stats for a question? Hint: You can pass the state to a function just like any other object. You'll know if your function works or doesn't because the tests will tell you if you break anything.

Can you write a single function `getNextStats` that each case can call that will return the next stats value? 

If you write these functions and replace all the code inside the cases with them, you're eliminating duplicate code without changing the way the code works. That is called refactoring, and it's a big part of Test Driven Development.

####Next Post
In the next post we will make the Stats Component that will show the stats to the user.