Card Selector
Tags: react, typescript, testing, tutorial
Published: 2020-01-17 04:50:07

In this post we are going to build the `Selector` component. The `Selector` will let the user select cards and subjects. We will add the new `CardAction` types that the `Selector` will need. We will also write the code for `CardContext` to handle those new actions.

![Selector in Action](https://thepracticaldev.s3.amazonaws.com/i/y5k3ef7eujaiqmk5381e.gif)

##User Stories
* The user sees a card and wants to change the answer. The user opens the card editor. The user selects the the card that they want to change. The user changes an that card and saves their changes.

* The user deletes a card.

* The user loads the app. The user sees all the cards they have written. The user selects the subject that they want to study. The program displays the cards in that subject in random order.

##Features
* a way the user can select cards
* To delete a card, you need to indicate which card you want to delete
* A button that displays subjects, and allows the user to select the subject

#The Selector Component
The `Selector` will let the user choose what card to look at. `Selector` will work in both scenes. We will put `Selector` on the left side of the screen. After we make `Selector` we are done building components for the app!

![Selector](https://thepracticaldev.s3.amazonaws.com/i/5wdojjvdsp9g7m8rqq5g.png)

##Where to Store the Data for Selector?
The features listed above require us to track what subject or subjects the user wants to display. We don't have a place to track subjects. So we need to add it somewhere. 

How would you solve the problem of storing subjects? The subject of each question is a string. What data structure would you use to store 0, 1, or many strings? Where would you keep it?

We are going to store the subjects in an array of strings. We are going to call this array `show`. We'll call the array `show` because it tells us what subjects to show the user. We are going to store `show` in the `CardState` that we keep in `CardContext`. We need to be able to refer to this array to write our tests, so we need to add it to the definition of `CardState` before we write the tests for `CardContext`.

We'll dispatch actions to the `CardContext` to add a subject to `show`, remove a subject from `show`, and to clear all subjects out of `show`. 

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

Add `show : string[]` to CardState.

```javascript
//the shape of the state that CardContext returns
export interface CardState {
    
    //the array of Card objects
    cards: Card[],
    
    //the index of the currently displayed card object
    current: number,
    
    //the dispatch function that accepts actions
    //actions are handled by the reducer in CardContext
    dispatch: (action: CardAction) => void

    //the array of subjects currently displayed
    show: string[]
};
```

Before we write the actions, change `getInitialState` in `CardContext/services` so that it returns a `show` array.

##Change getInitialState in CardContext services
>File: src/services/CardContext/services/index.ts
>Will Match: [src/services/CardContext/services/complete/index-2.ts](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/services/complete/index-2.ts)

Add `show : []` to the object returned by getInitialState.
```javascript
//a function that loads the cards from localStorage
//and returns a CardState object
export const getInitialState = () => ({
    //the cards that are displayed to the user
    //if loadedCards is undefined, use cards
    cards: loadedCards ? loadedCards : cards,

    //index of the currently displayed card
    current: 0,
    
    //placeholder for the dispatch function
    dispatch: (action:CardAction) => undefined,

    //the array of subjects to show the user
    show: []
} as CardState);
```

##The New Actions
We need some new CardActionTypes. We need CardContext to do new things that it hasn't done before. We'll add 
* select - to select a card 
* showAdd - add a subject to the show array
* showAll - clear the show array so that we show all subjects
* showRemove - remove a subject from the show array 

##Add Actions to CardActionTypes
>File: src/types.ts
>Will Match: [src/complete/types-10.ts](https://github.com/jacobwicks/flashcard/blob/master/src/complete/types-10.ts)

Add select, showAdd, showAll, and showRemove to the enum `CardActionTypes`.

```javascript
export enum CardActionTypes {
    delete = 'delete',
    next = 'next',
    new = 'new',
    save = 'save',
    select = 'select',
    showAdd = 'showAdd',
    showAll = 'showAll',
    showRemove = 'showRemove'
}
```

Now add the actions to the union type CardAction:
```javascript
export type CardAction =
    //deletes the card with matching question
    | { type: CardActionTypes.delete, question: string }

    //clears the writing component
    | { type: CardActionTypes.new }    
    
    //moves to the next card
    | { type: CardActionTypes.next }
    
    //saves a card
    | { type: CardActionTypes.save, answer: string, question: string, subject: string }
    
    //selects card
    | { type: CardActionTypes.select, question: string }
    
    //saves a card
    | { type: CardActionTypes.save, answer: string, question: string, subject: string }
    
    //adds a subject to the array of subjects to show
    | { type: CardActionTypes.showAdd, subject: string }
    
    //shows all subjects
    | { type: CardActionTypes.showAll }

    //removes a subject from the array of subjects to show
    | { type: CardActionTypes.showRemove, subject: string } 
```

All right. Now the actions have been defined. Next we will write the tests and the code for the `CardContext` reducer to handle the actions.

##CardContext reducer Tests 1-2: Select Actions
>File: src/services/CardContext/index.test.tsx
>Will Match: [src/services/CardContext/complete/test-11.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/test-11.tsx)

We'll test if the reducer handles select, showAdd, showAll, and showRemove actions. 

Write a comment for each test you plan to make:

```
//select should set the current index to the index of the selected card
//if the question is not found, returns state
//showAdd should add a single subject to the show array
//if the subject is already in show, the subject will not be added
//showAll should clear the show array
//showRemove should remove a single subject from the show array
```

Make some describe blocks inside the 'CardContext reducer' block. 

Name the first block 'select actions change current to the index of the card with the selected question.'

Name the second block 'Actions for showing subjects.' 
```javascript
   describe('select actions change current to the index of the card with the selected question', () => {
 
    //select should set the current index to the index of the selected card
    //if the question is not found, returns state
 });
   
    //actions that affect the show array
    describe('Actions for showing subjects', () => {
        //show add adds subjects to the array
        describe('showAdd', () => {
            //showAdd should add a single subject to the show array
            //if the subject is already in show, the subject will not be added

        });
        
        //showAll should clear the show array

        //showRemove should remove a single subject from the show array
    });
```

Write the test for the `select` case. Make a card `thirdCard`. Make a CardState with three cards in it `threeCardState`. Put `thirdCard` in `cards` at the last index. 


```javascript
it('select changes current to the index of the card with the selected question', () => {
        const answer = 'Example Answer';
        const question = 'Example Question';
        const subject = 'Example Subject';

        const thirdCard = {
            answer,
            question,
            subject
        };
       
        const threeCardState = {
            ...initialState,
            cards: [
                ...initialState.cards, 
                thirdCard
            ],
            current: 0
        };

        expect(threeCardState.cards.length).toBe(3);

        const selectAction = {
            type: CardActionTypes.select,
            question
        };

        const { current } = reducer(threeCardState, selectAction);

        expect(current).toEqual(2);
    });
```

Also write the test for a question that is not found in `cards`.
```javascript
//if the question is not found, returns state
        it('if no card matches the question, returns state', () => {
            const question = 'Example Question';
            
            expect(initialState.cards.findIndex(card => card.question === question)).toBe(-1);

            const selectAction = {
                type: CardActionTypes.select,
                question
            };

            const state = reducer(initialState, selectAction);

            expect(state).toEqual(initialState);
        });
```

![Select Fail](https://thepracticaldev.s3.amazonaws.com/i/4ojv1ymv2d401y8ne9fw.png)

Note that the test for returning state when no question is found passes. This test passes because there is no case to handle the `select` action yet. So the action is handled by the `default` case. The `default` case returns state.

##Pass CardContext reducer Tests 1-2: Select Actions
>File: src/services/CardContext/index.tsx
>Will Match: [src/services/CardContext/complete/index-10.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/index-10.tsx)

Add the `select` case to the reducer. 
```javascript
    case 'select' : {
        const { cards } = state;
        const { question } = action;

        if (!question) return state;            
        
        const current = cards.findIndex(card => card.question === question);

        if (current < 0 ) return state;
        
        return {
            ...state,
            current
        }
    }
```
![Select Pass](https://thepracticaldev.s3.amazonaws.com/i/eylczjtinlwj75nwe6rv.png)

##CardContext reducer Tests 3-4: showAdd Actions
>File: src/services/CardContext/index.test.tsx
>Will Match: [src/services/CardContext/complete/test-12.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/test-12.tsx)

The first test looks at the resulting show array and expects the item at index 0 to equal the added subject.

The second test uses the [toContain](https://jestjs.io/docs/en/expect#tocontainitem) assertion to check if the array contains the subject.

```javascript
//show add adds subjects to the array
        describe('showAdd', () => {
            //showAdd should add a single subject to the show array
            it('adds the selected subject to the show array', () => {
                expect(initialState.show).toHaveLength(0);

                const subject = 'Example Subject';

                const showAddAction = {
                    type: CardActionTypes.showAdd,
                    subject
                };

                const { show } = reducer(initialState, showAddAction);

                expect(show).toHaveLength(1);
                expect(show[0]).toEqual(subject);
            });

            //if the subject is already in show, the subject will not be added
            it('if the selected subject is already in the array, the subject will not be added', () => {
                const subject = 'Example Subject';

                const showWithSubjects = [
                    subject,
                    'Another Subject'
                ];

                const showState = {
                    ...initialState,
                    show: showWithSubjects
                };

                const showAddAction = {
                    type: CardActionTypes.showAdd,
                    subject
                };

                const { show } = reducer(showState, showAddAction);

                expect(show).toHaveLength(2);
                expect(show).toContain(subject);
            })
        });
```
![ShowAdd Fail](https://thepracticaldev.s3.amazonaws.com/i/c0xa26xeoylu565ui0n7.png)

##Pass CardContext reducer Tests 3-4: showAdd Actions
>File: src/services/CardContext/index.tsx
>Will Match: [src/services/CardContext/complete/index-11.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/index-11.tsx)

Use the [Array.includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) method to figure out if the subject is already in show. `Array.includes` returns a boolean value.

```javascript
       case 'showAdd': {
            const { subject } = action;
            const show = [...state.show];
            
            !show.includes(subject) && show.push(subject);

            return {
                ...state,
                show
            }
        }
```
![ShowAdd Pass](https://thepracticaldev.s3.amazonaws.com/i/0a6vvj8wo6i8i5ercn5t.png)

##CardContext reducer Test 5: showAll Actions
>File: src/services/CardContext/index.test.tsx
>Will Match: [src/services/CardContext/complete/test-13.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/test-13.tsx)

```javascript
//showAll should clear the show array
        it('showAll returns empty show array', () => {
            const showWithSubjects = [
                'Example Subject',
                'Another Subject'
            ];

            const showState = {
                ...initialState,
                show: showWithSubjects
            };

            const showAllAction = { type: CardActionTypes.showAll };

            const { show } = reducer(showState, showAllAction);

            expect(show).toHaveLength(0);
        });
```
![ShowAll Fail](https://thepracticaldev.s3.amazonaws.com/i/luqjrwxkdvezhctzybjr.png)

##Pass CardContext reducer Test 5: showAll Actions
>File: src/services/CardContext/index.tsx
>Will Match: [src/services/CardContext/complete/index-12.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/index-12.tsx)

To show all subjects, clear `show` array.

```javascript
        case 'showAll': {
            return {
                ...state,
                show: []
            }
        }
```
![ShowAll Pass](https://thepracticaldev.s3.amazonaws.com/i/0q2ra9kil45yfq2sbwts.png)

##CardContext reducer Test 6: showRemove Actions
>File: src/services/CardContext/index.test.tsx
>Will Match: [src/services/CardContext/complete/test-14.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/test-14.tsx)


```javascript
        //showRemove should remove a single subject from the show array
        it('showRemove removes the subject from show', () => {
            const subject = 'Example Subject';

                const showWithSubjects = [
                    subject,
                    'Another Subject'
                ];

                const showState = {
                    ...initialState,
                    show: showWithSubjects
                };

                const showRemoveAction = {
                    type: CardActionTypes.showRemove,
                    subject
                };

                const { show } = reducer(showState, showRemoveAction);

                expect(show).toHaveLength(1);
                expect(show).not.toContain(subject);
        });
```

![ShowRemove Fail](https://thepracticaldev.s3.amazonaws.com/i/3roi7d6dw8ksj00uiyg3.png)

##Pass CardContext reducer Test 6: showRemove Actions
>File: src/services/CardContext/index.tsx
>Will Match: [src/services/CardContext/complete/index-13.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/services/CardContext/complete/index-13.tsx)

Use Array.filter to remove the subject from `show`.
```javascript
        case 'showRemove': {
            const { subject } = action;
            const show = state.show.filter(subj => subj !== subject);

            return {
                ...state,
                show
            }
        }
```
![ShowRemove Pass](https://thepracticaldev.s3.amazonaws.com/i/8w2g6few8w9b53xzizqs.png)

Now the reducer in CardContext handles all the actions that we need to make the Selector work.

#Making the Selector
The `Selector` is the last component we'll make for the Flashcard App. The `Selector` will let the user select cards that they want to see. The `Selector` will also let the user select subjects that they want to see. 

As always, we'll use TDD to write the tests and the code. 

##Choose components
To let the user choose the questions we need to show the questions to the user. We want the user to be able to choose a single question and see it. We also want to let the user choose one or many subjects. And the user needs to be able to clear the list of subjects when they want to see cards from all the subjects at once.

We are going to use the [Sidebar](https://react.semantic-ui.com/modules/sidebar/#states-visible) and the [Menu](https://react.semantic-ui.com/collections/menu/) components from Semantic UI React. We will use these two components together to make a vertical menu that appears on the left side of the screen.

The `Sidebar` can hold `Menu Items`. We want to display a `Menu Item` for each subject, and when the user clicks on a subject, we will show the user a `Menu Item` for each card that has that subject. The `Menu Item` will show the question from the card. When the user clicks on a question we'll dispatch a `select` action to CardContext so that we can display that question to the user.

##Decide what to test
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-1.tsx)

We'll test if the `Sidebar` shows up. We expect to see `Menu Items` for each card subject inside the sidebar. Clicking a subject should expand that subject and show all the cards that have that subject. Clicking a card should select that card and set current index in `CardContext`.

Write a comment for each test you plan to make:
```javascript
//there is a sidebar
//the sidebar has a menu item that says 'subjects'
//clicking the 'subjects' menu item clears the selected subjects so the app will shows cards from all subjects
//the sidebar has menu items in it
//a menu item appears for each subject in the array cards in CardContext
//clicking on a menu item for a subject selects that subject
//clicking on a menu item for a subject expands that subject and shows a menu item with the question for each card in that subject
//clicking on a menu item for a card question selects that card
```

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

afterEach(cleanup);
```
A helper component `DisplaysCurrent` to display the value of current and show. We'll use Array.map to turn the array `show` into an array of divs that each contain a single subject. [React requires child components in an array to have a key](https://reactjs.org/docs/lists-and-keys.html). So each subject div gets a key prop.

```javascript
const DisplaysCurrent = () => {
    const { current, show } = useContext(CardContext);
    return(
        <div>
            <div data-testid='current'>{current}</div>
            <div data-testid='show'>
                {show.map(subject => <div key={subject}>{subject}</div>)}
            </div>
        </div>
    ) 
};
```

A helper function `renderSelector` to render the `Selector` inside of `CardProvider`. Accepts an optional  `testState`. Accepts an optional `child` component.

```javascript
const renderSelector = (
    testState?: CardState, 
    child?: JSX.Element 
    ) => render(
    <CardProvider testState={testState}>
        <Selector/>
        {child}
    </CardProvider>
);
```

##Selector Test 1: Has a Sidebar
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-1.tsx)

```javascript
//there is a sidebar
it('has a sidebar', () => {
    const { getByTestId } = renderSelector();
    const sidebar = getByTestId('sidebar');
    expect(sidebar).toBeInTheDocument();
});
```
This test fails because we haven't made the `Selector` yet.

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

Imports. We'll use all of these eventually.
```javascript
import React, { useContext } from 'react';
import {
    Menu,
    Sidebar
} from 'semantic-ui-react';
import { CardContext } from '../../services/CardContext';
import { CardActionTypes } from '../../types';
```

Make the `Selector` component.
```javascript
const Selector = () => {
    return (
        <Sidebar
        as={Menu}
        data-testid='sidebar'
        style={{top: 50}}
        vertical
        visible
        width='thin'
      >
      </Sidebar>
    )    
};

export default Selector;
```
![Selector Has a Sidebar](https://thepracticaldev.s3.amazonaws.com/i/9dt7ihm8cuqnfnl5rm3m.png)

##Selector Test 2: Has Subjects Menu Item
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-2.tsx) 


Make a describe block named 'the subjects menu item.' We'll test for a menu item that says subjects. 

```javascript
describe('the subjects menu item', () => {
    //there is a menu item that says 'subjects'
        it('has a subjects menu item', () => {
            const { getByText } = renderSelector();
            //the first menu item in the selector says 'Subjects' on it
            //if we can find that text, we know the sidebar is showing up
            const selector = getByText(/subjects/i);
            expect(selector).toBeInTheDocument();
        });
        
        //clicking the 'subjects' menu item clears the selected subjects so the app will shows cards from all subjects    
});
```

![Menu item fail](https://thepracticaldev.s3.amazonaws.com/i/167hd8pi88mfvnehnniw.png)


##Pass Selector Test 2: Has Subjects Menu Item
>File: src/components/Selector/index.tsx
>Will Match: [src/components/Selector/complete/index-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/index-2.tsx) 

Make the `Selector` return a `Menu Item` that says 'Subjects.'
```javascript
        <Sidebar
        as={Menu}
        data-testid='sidebar'
        style={{top: 50}}
        vertical
        visible
        width='thin'
      >
        <Menu.Item as='a'>Subjects</Menu.Item>
      </Sidebar>
```

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/crx7lhhj1igqt1rfce6z.png)


##Selector Test 3: Clicking Subjects Menu Item Clears Show
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-3.tsx) 

In this test we render the helper component `DisplaysCurrent`. We can determine how many items are in the `show` array by looking at the div with testId 'show' in `DisplaysCurrent` 'children' property and counting its children.
 
```javascript
 //clicking the 'subjects' menu item clears the selected subjects so the app will shows cards from all subjects
        it('clicking the subjects menu clears show', () => {
            const showSubjects = ['First Subject', 'Second Subject'];
            const showState = {
                ...initialState,
                show: showSubjects
            };

            const { getByText, getByTestId } = renderSelector(showState, <DisplaysCurrent />);
 
            const show = getByTestId('show');
            expect(show.children).toHaveLength(2);

            const subjects = getByText(/subjects/i);
            fireEvent.click(subjects);

            expect(show.children).toHaveLength(0);
        });   
```
![Clear Show Fail](https://thepracticaldev.s3.amazonaws.com/i/5p4b8nkkbryrlt3bigxe.png)

##Pass Selector Test 3: Clicking Subjects Menu Item Clears Show
>File: src/components/Selector/index.tsx
>Will Match: [src/components/Selector/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/index-3.tsx) 

Get `dispatch` from `CardContext`. Add an onClick function to the 'Subjects' `Menu.Item` that dispatches a `showAll` action to `CardContext`.

```javascript
const Selector = () => {
    const { dispatch } = useContext(CardContext);
    
    return (
        <Sidebar
        as={Menu}
        data-testid='sidebar'
        style={{top: 50}}
        vertical
        visible
        width='thin'
      >
        <Menu.Item as='a' onClick={() => dispatch({type: CardActionTypes.showAll})}>Subjects</Menu.Item>
      </Sidebar>
    )    
};
```

![Clicking Subjects Clears Show](https://thepracticaldev.s3.amazonaws.com/i/pflxtyx2n0ranazihj0x.png)

##Selector Tests 4-7: Renders a Menu Item for Each Subject
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-4.tsx) 


There should be a menu item for each subject. We are going to test 0 cards, then use test.each to test for 1-3 cards.

Make a describe block named 'when there are cards, the sidebar has a menu item for each subject.'

```javascript
//the sidebar has menu items in it
describe('when there are cards, the sidebar has a menu item for each subject', () => {
     //test 0 cards
    //test 1-3 cards with different subjects
    //1-3 cards show correct number of subject menu items
    //1-3 cards show subject menu items with correct names
});
```
Test for 0 cards. Look at the children property of sidebar to figure out how many menu items are being rendered.

```javascript
//the sidebar has menu items in it
describe('when there are cards, the sidebar has a menu item for each subject', () => {
     //test 0 cards
    it('when there are no cards, there is only the "subjects" menu item', () => {
        const noCards = {
            ...initialState,
            cards: []
        };

        const { getByTestId } = renderSelector(noCards);
        const sidebar = getByTestId('sidebar');
        
        expect(sidebar.children).toHaveLength(1);
    });
```

Make a `getCard` function that takes a number and returns a card object. We'll use `getCard` to create a `CardState` with cards with different subjects. The expressions inside of the backticks are [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).
```javascript
//getCard returns a card object
    //the subject is the number argument as a string 
    const getCard = (number: number) => ({
        question: `${number}?`,
        answer: `${number}!`,
        subject: number.toString()
    });
```

Make an array `numberOfSubjects`. We'll pass this array to `test.each`. You've already seen `test.each` accept an array of arrays. If you pass `test.each` an array of 'primitives,' like numbers or strings, `test.each` [will treat it as an array of arrays](https://jestjs.io/docs/en/api#testeachtablename-fn-timeout). 
```javascript
    //array 1, 2, 3 will get treated as [[1],[2],[3]] by test.each
    const numberOfSubjects = [1, 2, 3];
```

Test if there's a `Menu Item` for each subject. Make an empty array `cards`. Use a [for loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) to fill `cards` with `Card` objects by calling `getCard` repeatedly.

Make a `CardState` object named `subjectState` using the `cards` array. Then call `renderSelector` and test how many children sidebar is rendering.

```javascript
    //test 1-3 cards with different subjects
    //1-3 cards show correct number of subject menu items
    test.each(numberOfSubjects)
    //printing the title uses 'printf syntax'. numbers are %d, not %n
    ('%d different subjects display correct number of subject menu items', 
    //name the arguments, same order as in the array we generated
    (number) => {
        //generate array of cards
        const cards : Card[] = [];

        for (let i = 1; i <= number; i++) {
            cards.push(getCard(i));
        };
        
        //create state with cards with subjects
        const subjectState = {
            ...initialState,
            cards
        };
        
        //render selector with the state with the subjects
        const { getByTestId } = renderSelector(subjectState);
        const sidebar = getByTestId('sidebar');

        expect(sidebar.children).toHaveLength(number + 1);
    });
```

Test if the names are right. We can make Jest assertions inside of a for loop.
```javascript
    //1-3 cards show subject menu items with correct names
    test.each(numberOfSubjects)
    ('%d different subjects display menu items with correct names', 
    (number) => {
        //generate array of cards
        const cards : Card[] = [];

        for (let i = 1; i <= number; i++) {
            cards.push(getCard(i));
        };
        
        //create state with cards with subjects
        const subjectState = {
            ...initialState,
            cards
        };
        
        //render selector with the state with the subjects
        const { getByTestId, getByText } = renderSelector(subjectState);
        const sidebar = getByTestId('sidebar');

        expect(sidebar.children).toHaveLength(number + 1);

        for (let i = 1; i <= number; i++) {
            const numberItem = getByText(i.toString());
            expect(numberItem).toBeInTheDocument();
        };
        
    });
```
![Subject Menu Item Fail](https://thepracticaldev.s3.amazonaws.com/i/pcagigrgw25n970ukjnp.png)

##Pass Selector Tests 4-7: Renders a Menu Item for Each Subject
>File: src/components/Selector/index.tsx
>Will Match: [src/components/Selector/complete/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/index-4.tsx) 

Get `cards` from `CardContext`.

Use Array.map to get an array `subjectArray` of just the subject from each card.

Create a new Set `subjectSet` from `subjectArray`. A [set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) is an object that only holds unique values. So `subjectSet` will only contain one copy of each unique subject, regardless of how many times that subject appeared in `subjectArray`.

Use [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) to make an array `subjects` out of the set object `subjectSet`. Mildly interesting fact that you don't need to know or understand: We could also use the spread operator to make this array, [but we would have to change some TypeScript settings](https://github.com/Microsoft/TypeScript/issues/21058).

Use [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to sort `subjects` into alphabetical order. `Array.sort` takes a function, uses the function to compares the objects in an array, and manipulates the array order. 

Inside our sort function we cast the strings toLowerCase and use the [string.localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) method to get the correct sort result. If you don't use `toLowerCase` then capitalization will result in incorrect sorting. If you don't use `localeCompare` then numbers won't sort correctly.

Once we have `subjects`, our correctly sorted array of all the unique subjects from all the cards, we use `Array.map` to turn `subjects` into `Menu.Item`s.

```javascript
const Selector = () => {
    const { cards, dispatch } = useContext(CardContext);
    
    const subjectArray = cards.map(card => card.subject);
    
    const subjectSet = new Set(subjectArray);
    
    const subjects = Array.from(subjectSet)
                    .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
    return (
        <Sidebar
        as={Menu}
        data-testid='sidebar'
        style={{top: 50}}
        vertical
        visible
        width='thin'
      >
          <Menu.Item as='a' onClick={() => dispatch({type: CardActionTypes.showAll})}>Subjects</Menu.Item>
        {subjects.map(subject => <Menu.Item key={subject} content={subject}/>)}
      </Sidebar>
    )    
};
```

![Correct Names and Numbers pass](https://thepracticaldev.s3.amazonaws.com/i/syaup65tmyemupdcsz1o.png)


##Selector Test 8: Clicking Subject Menu Item Selects That Subject
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-5.tsx) 

We call `renderSelector` with the helper component `DisplaysCurrent`. By looking at the children of the `show` div, we can check what subjects are rendered before and after subject `Menu.Item`s are clicked.
 
```javascript
//clicking on a menu item for a subject selects that subject
it('clicking a subject item selects that subject', () => {
    const { cards } = initialState;
    expect(cards).toHaveLength(2);

    const first = cards[0];
    const second = cards[1];
    expect(first.subject).toBeTruthy();
    expect(second.subject).toBeTruthy();
    expect(first.subject).not.toEqual(second.subject);

    const { getByText, getByTestId } = renderSelector(initialState, <DisplaysCurrent />);
    
    const show = getByTestId('show');
    expect(show.children).toHaveLength(0);

    const firstSubject = getByText(first.subject);
    fireEvent.click(firstSubject);
    
    expect(show.children).toHaveLength(1);
    expect(show.children[0]).toHaveTextContent(first.subject.toString());

    const secondSubject = getByText(second.subject);
    fireEvent.click(secondSubject);

    expect(show.children).toHaveLength(2);
    expect(show.children[1]).toHaveTextContent(second.subject.toString());
});
```

![Click to Select Fail]
(https://thepracticaldev.s3.amazonaws.com/i/291ljdnb2rn3n4tetsmq.png)

##Pass Selector Test 8: Clicking Subject Menu Item Selects That Subject
>File: src/components/Selector/index.tsx
>Will Match: [src/components/Selector/complete/index-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/index-5.tsx) 


Let's also make the 'Subjects' menu item display how many subjects are selected. Get `show` from the cardContext.
```javascript
    const { cards, dispatch, show } = useContext(CardContext);
```

Add the expression 
```javascript
{!!show.length && \`: ${show.length}\`}
```
to the 'Subjects' Menu.Item. `!!show.length` casts the length property of the `show` array to boolean, so if there's anything in `show` it will return true. `&&` means that if the first expression returns true, the second expression will be evaluated. `: ${show.length}` is a template literal that will display a colon followed by the number of subjects in the `show` array.
 
Add an onClick function to the `Menu.Item` returned from `subjects.map`. The onClick function should dispatch a `showAdd` action.

```javascript
<Sidebar
        as={Menu}
        data-testid='sidebar'
        style={{top: 50}}
        vertical
        visible
        width='thin'
      >
        <Menu.Item as='a' onClick={() => dispatch({type: CardActionTypes.showAll})}>
            Subjects{!!show.length && `: ${show.length}`}
        </Menu.Item>
        {subjects.map(subject => 
            <Menu.Item 
                content={subject}
                key={subject} 
                onClick={() => dispatch({type: CardActionTypes.showAdd, subject})}
            />)}
      </Sidebar>
```

![Clicking Select Pass](https://thepracticaldev.s3.amazonaws.com/i/wa5dc5hr87dnujsv16jl.png)

#Subject Component
The next test for the Selector is: 
//clicking on a menu item for a subject expands that subject and shows a menu item with the question for each card in that subject

We are making a Subject component that will do all of that.
##Features of Subject
* Displays a subject to user
* clicking on subject expands subject to show each card in subject
* clicking on a card selects that card
* clicking on an expanded subject deselects that subject and collapses the subject, hiding the cards in that subject

##What to test:
Write a comment for each test.
```javascript
//displays the subject as a menu item
//when a menu item is clicked clicked it should expand to show a menu item for each card/question in the subject
//if the subject is already expanded when it is clicked then it should collapse
//clicking a card menuItem selects the card
```

##Subject Test 1: Displays Subject as Menu Item
>File: src/components/Selector/components/Subject/index.test.tsx
>Will Match: [src/components/Selector/components/Subject/complete/test-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/test-1.tsx) 

```javascript
import React, { useContext } from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { CardContext, CardProvider, initialState } from '../../../services/CardContext';
import Subject from './index';
import { CardState } from '../../../types';

afterEach(cleanup);
```

```javascript
const renderSubject = (
    subject: string,
    testState?: CardState, 
    child?: JSX.Element 
    ) => render(
    <CardProvider testState={testState}>
        <Subject subject={subject}/>
        {child}
    </CardProvider>
);
```
The test
```javascript
//displays the subject as a menu item
it('shows the subject on screen', () => {
    const subject = initialState.cards[0].subject;
    const { getByText } = renderSubject(subject);
    const subjectDisplay = getByText(subject);
    expect(subjectDisplay).toBeInTheDocument();
});
```
##Pass Subject Test 1: Displays Subject as Menu Item
>File: src/components/Selector/components/Subject/index.tsx
>Will Match: [src/components/Selector/components/Subject/complete/index-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/index-1.tsx) 

Make the `Subject` component include a `Menu.Item`. 
```javascript
import React, { Fragment, useContext } from 'react';
import { Icon, Menu } from 'semantic-ui-react';
import { CardContext } from '../../../../services/CardContext';
import { CardActionTypes } from '../../../../types';

const Subject = ({
    subject
  }: {
    subject: string
  }) => <Menu.Item as='a'>
      <Icon name='list'/>
      {subject}
  </Menu.Item>

export default Subject;
```
![Subject Pass](https://thepracticaldev.s3.amazonaws.com/i/v0dnuzcmum3we0qhw4ww.png)

##Subject Tests 2-4: Clicking Subject Expands, Shows Cards
>File: src/components/Selector/components/Subject/index.test.tsx
>Will Match: [src/components/Selector/components/Subject/complete/test-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/test-2.tsx) 

Make a `getCard` function that returns a `Card` object.

Make a `numberOfCards` array to pass to `test.each`. Inside `test.each` use a for loop to call `getCards` and generate a `subjectState` with an array of cards.

Click the subject, test how many children are rendered after the click. 

Use a for loop to assert that each child card appears in the document.

```javascript
describe('expanded', () => {
    //getCard returns a card object
    //the subject is always the same 
    const getCard = (number: number) => ({
        question: `${number}?`,
        answer: `${number}!`,
        subject: 'subject'
    });
    
    //array 1, 2, 3 will get treated as [[1],[2],[3]] by test.each
    const numberOfCards = [1, 2, 3];
    
    //when clicked it should expand to show a menu item for each question in the subject
    //1-3 cards show correct number of card menu items
    test.each(numberOfCards)
    //printing the title uses 'printf syntax'. numbers are %d, not %n
    ('%d different cards display correct number of card menu items', 
    //name the arguments, same order as in the array we generated
    (number) => {
        //generate array of cards
        const cards : Card[] = [];

        for (let i = 1; i <= number; i++) {
            cards.push(getCard(i));
        };
        
        //create state with cards with subjects
        const subjectState = {
            ...initialState,
            cards
        };
        
        //render selector with the state with the subjects
        const { getAllByText, getByText } = renderSubject('subject', subjectState);
        const subject = getByText('subject');
        fireEvent.click(subject);

        const questions = getAllByText(/\?/);
        expect(questions).toHaveLength(number);

        for (let i = 1; i <= number; i++) {
            const numberItem = getByText(`${i.toString()}?`);
            expect(numberItem).toBeInTheDocument();
        };

    });
});
```
![When Collapsed Click Expands Fail](https://thepracticaldev.s3.amazonaws.com/i/6xl0yd51r4hnpdtk0dvr.png)

##Pass Subject Tests 2-4: Clicking Subject Expands, Shows Cards
>File: src/components/Selector/components/Subject/index.tsx
>Will Match: [src/components/Selector/components/Subject/complete/index-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/index-2.tsx) 

Get `cards`, `dispatch`, and `show` from `CardContext`. 

Use `Array.includes` to figure out if the subject is in the array `show` and should be `expanded`.

Use `Array.filter` to get an array of just the cards with this subject.

Declare `cardsChild`, an array of `Menu.Items` generated by using Array.map on the array `subjectCards`. 

Put a [React Fragment](https://reactjs.org/docs/fragments.html) around the component. The `Fragment` gives us somewhere to render `cardsChild` when we want to.
 
When expanded is true, render `cardsChild`.

```javascript
const Subject = ({
    subject
  }: {
    subject: string
  }) =>  {
    const { cards, dispatch, show } = useContext(CardContext);
  
    //true if the subject is in the array show
    const expanded = show.includes(subject);

    //use filter to pull only the cards that have this subject
    const subjectCards = cards
    .filter(card => card.subject === subject)

    //cardsChild will return an array of <Menu.Item/> components
    const cardsChild = subjectCards
    .map(card => {
      const { question } = card;
      return <Menu.Item 
              content={question}
              as='a' 
              key={question}
            />
        });

    return (
        <Fragment>
            <Menu.Item as='a' onClick={() => dispatch({type: CardActionTypes.showAdd, subject})}> 
                <Icon name='list'/>
                {subject}
            </Menu.Item>
            {expanded && cardsChild}
        </Fragment>
    )};
```

![When Collapsed Click Expands Pass](https://thepracticaldev.s3.amazonaws.com/i/0178geawflz6p8purap0.png)

##Subject Test 5: Clicking on a Menu Item with a Question Selects the Card With That Question
>File: src/components/Selector/components/Subject/index.test.tsx
>Will Match: [src/components/Selector/components/Subject/complete/test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/test-3.tsx) 

Make a helper component `DisplaysCurrent` to display the current index from `CardContext`. Call `renderSubject` with the helper component. 

Find and click a card `Menu.Item`. Assert that current should match the index of that card in `cards`.

```javascript
describe('Expanded', () => {
    //clicking a card menuItem selects the card
    it('clicking on a question selects the card for that question', () => {        
        const { question, subject } = initialState.cards[1];
        const showState = {
            ...initialState,
            current: 0,
            show: [subject]
        };

        const DisplaysCurrent = () => {
            const { current } = useContext(CardContext);
            return <div data-testid='current'>{current}</div>
        };
        
        const { getByTestId, getByText } = renderSubject(subject, showState, <DisplaysCurrent />)
        
        const current = getByTestId('current');
        expect(current).toHaveTextContent('0');

        const menuItem = getByText(question);
        fireEvent.click(menuItem);
        
        expect(current).toHaveTextContent('1'); 
    });

    //if the subject is already expanded when it is clicked then it should collapse
})
```

![Clicking Selects Fail](https://thepracticaldev.s3.amazonaws.com/i/09yb567kaaxjwbpytqlv.png)

##Pass Subject Test 5: Clicking on a Menu Item with a Question Selects the Card With That Question
>File: src/components/Selector/components/Subject/index.tsx
>Will Match: [src/components/Selector/components/Subject/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/index-3.tsx) 


Add an onClick function to the `Menu.Item` in `cardChild`. The onClick function should dispatch a `select` action to `CardContext`.

```javascript
 <Menu.Item 
              content={question}
              as='a' 
              key={question}
              onClick={() => dispatch({type: CardActionTypes.select, question})}
            />
```

![Clicking Selects Pass](https://thepracticaldev.s3.amazonaws.com/i/lc53jwy53f3iakxv2i5h.png)

##Subject Test 6: Clicking on an Expanded Subject Collapses that Subject
>File: src/components/Selector/components/Subject/index.test.tsx
>Will Match: [src/components/Selector/components/Subject/complete/test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/test-4.tsx) 

This test just looks for one card. How would you use `test.each` to test for many cards?

```javascript
//if the subject is already expanded when it is clicked then it should collapse
    it('if already expanded, it collapses when clicked ', () => {
        const { subject, question } = initialState.cards[0];
        expect(subject).toBeTruthy();

        const showState = {
            ...initialState, 
            //subject is in the show array
            show: [subject]
        };
    
        const { getByText } = renderSubject(subject, showState);
        
        //because subject is in the show array, <Subject> should be expanded
        //meaning, it should show a menu item for each card in the subject
        const questionItem = getByText(question);
        expect(questionItem).toBeInTheDocument();
        
        const subjectItem = getByText(subject);
        fireEvent.click(subjectItem);
      
        expect(questionItem).not.toBeInTheDocument();
      });
```

![Expanded Click Collapse Fail](https://thepracticaldev.s3.amazonaws.com/i/ux31386nizcroclh1w9j.png)

##Pass Subject Test 6: Clicking on an Expanded Subject Collapses that Subject
>File: src/components/Selector/components/Subject/index.tsx
>Will Match: [src/components/Selector/components/Subject/complete/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/index-4.tsx) 

Use the ternary operator to dispatch a `showRemove` action if the subject is expanded, and a `showAdd` action if the sucbject is not expanded.
 
```javascript
    return (
        <Fragment>
            <Menu.Item as='a'
                onClick={() => expanded 
                    ? dispatch({type: CardActionTypes.showRemove, subject})
                    : dispatch({type: CardActionTypes.showAdd, subject})}> 
                <Icon name='list'/>
                {subject}
            </Menu.Item>
            {expanded && cardsChild}
        </Fragment>
```

![Expanded Click Collapse Pass](https://thepracticaldev.s3.amazonaws.com/i/fi8uyswx7bmje0cn8f65.png)

##Refactor Subject- Change Some Implementation Details
>File: src/components/Selector/components/Subject/index.tsx
>Will Match: [src/components/Selector/components/Subject/complete/index-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/components/Subject/complete/index-5.tsx) 

Get current from CardContext so we can know what the current card is. Declare a const currentCard.

```javascript
    const { cards, current, dispatch, show } = useContext(CardContext);
  
    const currentCard = cards[current];
```

Use Array.sort to sort the array of cards alphabetically by question.
```javascript
//use filter to pull only the cards that have this subject
    const subjectCards = cards
    .filter(card => card.subject === subject)
    //.sort will put the cards in alphabetical order by question
    .sort((a, b) => 
      a.question.toLowerCase().localeCompare(b.question.toLowerCase()))
```

How would you write a test to make sure that the cards are in alphabetical order by question?

Mark the card as active if it's the current card. This will highlight the card on the screen.
```javascript
    <Menu.Item 
      active={!!currentCard && question === currentCard.question}
      as='a'
      content={question}
      key={question}
      onClick={() => dispatch({type: CardActionTypes.select, question})}
     />
```

Mark the subject as active if it has the subject of the current card. This will highlight the subject on the screen.
```javascript
        <Fragment>
            <Menu.Item as='a'
                active={!!currentCard && currentCard.subject === subject}
                onClick={() => expanded 
                    ? dispatch({type: CardActionTypes.showRemove, subject})
                    : dispatch({type: CardActionTypes.showAdd, subject})}> 
                <Icon name='list'/>
                {subject}
            </Menu.Item>
            {expanded && cardsChild}
        </Fragment>
```

Ok, `Subject` is done!

##Selector Tests 9-12: Add Subject to Selector
>File: src/components/Selector/index.test.tsx
>Will Match: [src/components/Selector/complete/test-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/test-6.tsx) 

The test for the `Selector` expanding to show the cards in a subject is almost the same when we use the `Subject` component, but now we call `renderSelector`.
 
```javascript
//clicking on a menu item for a subject expands that subject and shows a menu item with the question for each card in that subject
describe('When a subject is clicked it expands, shows menu item for each card', () => {
    //getCard returns a card object
    //the subject is always the same 
    const getCard = (number: number) => ({
        question: `${number}?`,
        answer: `${number}!`,
        subject: 'subject'
    });
    
    //array 1, 2, 3 will get treated as [[1],[2],[3]] by test.each
    const numberOfCards = [1, 2, 3];
    
    //when clicked it should expand to show a menu item for each question in the subject
    //1-3 cards show correct number of card menu items
    test.each(numberOfCards)
    //printing the title uses 'printf syntax'. numbers are %d, not %n
    ('%d different cards display correct number of card menu items', 
    //name the arguments, same order as in the array we generated
    (number) => {
        //generate array of cards
        const cards : Card[] = [];

        for (let i = 1; i <= number; i++) {
            cards.push(getCard(i));
        };
        
        //create state with cards with subjects
        const subjectState = {
            ...initialState,
            cards
        };
        
        //render selector with the state with the subjects
        const { getAllByText, getByText } = renderSelector(subjectState);
        const subject = getByText('subject');
        fireEvent.click(subject);

        const questions = getAllByText(/\?/);
        expect(questions).toHaveLength(number);

        for (let i = 1; i <= number; i++) {
            const numberItem = getByText(`${i.toString()}?`);
            expect(numberItem).toBeInTheDocument();
        };
    });
});
```

![Selector Click Expand Fail](https://thepracticaldev.s3.amazonaws.com/i/g9apptmp3f85m9qolb9y.png)

As is the test for clicking on a question selecting the card.
```javascript
//clicking on a menu item for a card question selects that card
it('clicking on a question selects the card for that question', () => {        
    const { question, subject } = initialState.cards[1];
    const showState = {
        ...initialState,
        current: 0,
        show: [subject]
    };

    const DisplaysCurrent = () => {
        const { current } = useContext(CardContext);
        return <div data-testid='current'>{current}</div>
    };
    
    const { getByTestId, getByText } = renderSelector(showState, <DisplaysCurrent />)
    
    const current = getByTestId('current');
    expect(current).toHaveTextContent('0');

    const menuItem = getByText(question);
    fireEvent.click(menuItem);
    
    expect(current).toHaveTextContent('1'); 
});
```

![Selector Click Select Fail](https://thepracticaldev.s3.amazonaws.com/i/zqh0anzdf9qq4rusetir.png)

##Pass Selector Tests 9-11: Add Subject to Selector
>File: src/components/Selector/index.tsx
>Will Match: [src/components/Selector/complete/index-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/Selector/complete/index-6.tsx) 

Import `Subject`.
```javascript
import Subject from './components/Subject';
```
Instead of mapping to a `Menu.Item`, map to a `Subject`.
```javascript
{subjects.map(subject => <Subject key={subject} subject={subject}/>)}
```

![Selector All Pass](https://thepracticaldev.s3.amazonaws.com/i/0c3kdusdquyhpkgkc2ih.png)

#Add Selector To App
Now let's add the `Selector` to the App so the user can use it to select subjects and cards.

##App Test 1: Has Selector
>File: src/App.test.tsx
>Will Match: [src/complete/test-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/test-6.tsx) 

Find the `Selector`'s sidebar by testId. 
```javascript
//shows the Selector
it('shows the Selector', () => {
  const { getByTestId } = render(<App/>);
  const selector = getByTestId('sidebar');
  expect(selector).toBeInTheDocument();
});
```
![App Fail](https://thepracticaldev.s3.amazonaws.com/i/3e59y7hj4i9u8kjppepe.png)

##Pass App Test 1: Has Selector
>File: src/App.tsx
>Will Match: [src/complete/app-7.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/app-7.tsx) 

Import `Selector`.
```javascript
import Selector from './components/Selector';
```

Add `Selector` to the App.
```javascript
    return (
      <CardProvider>
        <StatsProvider>
          <NavBar showScene={showScene} setShowScene={setShowScene} />
          <Selector/>
          {showScene === SceneTypes.answering && <Answering />}
          {showScene === SceneTypes.writing && <Writing/>}
        </StatsProvider>
      </CardProvider>
    )};
```

Tests all pass, but the snapshot fails.
![App Snapshot Fail](https://thepracticaldev.s3.amazonaws.com/i/g2vccuttdwzcu6lh59qg.png)

Update your snapshot.

![Snapshot Updated](https://thepracticaldev.s3.amazonaws.com/i/fxj6wkemb4r6cfjbhv2v.png)

Hit a to run all the tests:

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

Wow! You wrote 13 test suites and 126 tests! But I bet it only felt like 100, right? Good job!

![App In Action](https://thepracticaldev.s3.amazonaws.com/i/agol222kjcx37ay4kvpb.png)

####Next Post: Finishing Touches
In the final post, we'll write some code to shuffle the cards and display only cards from selected subjects.