diff --git a/README.md b/README.md
index d5f75a62d2caa98e27e121c428e6f51de21898f3..1f49dca4f60a4fd3c0f2039944a8c81dadbf7e5d 100644
--- a/README.md
+++ b/README.md
@@ -86,6 +86,12 @@ To run unit tests in watch mode (watches for code changes and continuously runs
 npm run test:unit -- --watch
 ```
 
+You can run a single file of integration tests (in this example, the `button` tests):
+
+```bash
+$(npm bin)/jest --runInBand test/unit/components/button.test.jsx
+```
+
 #### Running integration tests
 
 Integration tests use a headless browser to manipulate the actual html and javascript that the repo
diff --git a/src/lib/hash-parser-hoc.jsx b/src/lib/hash-parser-hoc.jsx
index 2cb90a526c5e73dfc422e813a5742584a40b1bbb..611f6769923f81e02489b1431d1b9874abdf8a1f 100644
--- a/src/lib/hash-parser-hoc.jsx
+++ b/src/lib/hash-parser-hoc.jsx
@@ -42,7 +42,7 @@ const HashParserHOC = function (WrappedComponent) {
         handleHashChange () {
             const hashMatch = window.location.hash.match(/#(\d+)/);
             const hashProjectId = hashMatch === null ? defaultProjectId : hashMatch[1];
-            this.props.setProjectId(hashProjectId);
+            this.props.setProjectId(hashProjectId.toString());
             if (hashProjectId !== defaultProjectId) {
                 this.setState({hideIntro: true});
             }
diff --git a/src/lib/project-fetcher-hoc.jsx b/src/lib/project-fetcher-hoc.jsx
index 4d850b68109fbff61b4842f2bae3ca396718e7f1..884a77cfc6885a6326017f28c3d63a368b64ab76 100644
--- a/src/lib/project-fetcher-hoc.jsx
+++ b/src/lib/project-fetcher-hoc.jsx
@@ -40,7 +40,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
                 props.projectId !== null &&
                 typeof props.projectId !== 'undefined'
             ) {
-                this.props.setProjectId(props.projectId);
+                this.props.setProjectId(props.projectId.toString());
             }
         }
         componentDidUpdate (prevProps) {
diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx
index 065a0602abbe568af67d32f775db195683860b09..83ccd12c1526cab5f23fb5a91fb0939377d9c2e6 100644
--- a/src/lib/project-saver-hoc.jsx
+++ b/src/lib/project-saver-hoc.jsx
@@ -39,7 +39,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
             if (this.props.isCreating && !prevProps.isCreating) {
                 this.storeProject()
                     .then(response => {
-                        this.props.onCreated(response.id);
+                        this.props.onCreated(response.id.toString());
                     })
                     .catch(err => {
                         // NOTE: should throw up a notice for user
diff --git a/src/reducers/project-state.js b/src/reducers/project-state.js
index b69cfc5244d93c4fa59e493dc5d47ed61417cfba..56e5bb1284dd8a030e4de74660dc5585a7c5fd0d 100644
--- a/src/reducers/project-state.js
+++ b/src/reducers/project-state.js
@@ -17,7 +17,7 @@ const START_LOADING_VM_FILE_UPLOAD = 'scratch-gui/project-state/START_LOADING_FI
 const START_SAVING = 'scratch-gui/project-state/START_SAVING';
 const START_SAVING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_SAVING_BEFORE_CREATING_NEW';
 
-const defaultProjectId = 0; // hardcoded id of default project
+const defaultProjectId = '0'; // hardcoded id of default project
 
 const LoadingState = keyMirror({
     NOT_LOADED: null,
@@ -83,7 +83,7 @@ const reducer = function (state, action) {
         if (state.loadingState === LoadingState.CREATING_NEW) {
             return Object.assign({}, state, {
                 loadingState: LoadingState.SHOWING_WITH_ID,
-                id: action.id
+                projectId: action.id
             });
         }
         return state;
@@ -160,8 +160,8 @@ const reducer = function (state, action) {
         }
         return state;
     case SET_PROJECT_ID:
-        // if we were already showing something, only fetch project if the
-        // project id has changed. This prevents re-fetching projects unnecessarily.
+        // if we were already showing a project, and a different projectId is set, only fetch that project if
+        // projectId has changed. This prevents re-fetching projects unnecessarily.
         if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
             if (state.projectId !== action.id) {
                 return Object.assign({}, state, {
diff --git a/test/unit/reducers/project-state-reducer.test.js b/test/unit/reducers/project-state-reducer.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..920999ffc45e4678731cdac028ebbd79ac1a7c2e
--- /dev/null
+++ b/test/unit/reducers/project-state-reducer.test.js
@@ -0,0 +1,242 @@
+/* eslint-env jest */
+import projectStateReducer from '../../../src/reducers/project-state';
+import {
+    LoadingState,
+    onCreated,
+    onError,
+    onFetchedProjectData,
+    onLoadedProject,
+    onProjectUploadStarted,
+    onUpdated,
+    requestNewProject,
+    saveProject,
+    setProjectId
+} from '../../../src/reducers/project-state';
+
+test('initialState', () => {
+    let defaultState;
+    /* projectStateReducer(state, action) */
+    expect(projectStateReducer(defaultState, {type: 'anything'})).toBeDefined();
+    expect(projectStateReducer(defaultState, {type: 'anything'}).errStr).toBe(null);
+    expect(projectStateReducer(defaultState, {type: 'anything'}).projectData).toBe(null);
+    expect(projectStateReducer(defaultState, {type: 'anything'}).projectId).toBe(null);
+    expect(projectStateReducer(defaultState, {type: 'anything'}).loadingState).toBe(LoadingState.NOT_LOADED);
+});
+
+test('onCreated with projectId type string shows project with that id', () => {
+    const initialState = {
+        projectId: null,
+        loadingState: LoadingState.CREATING_NEW
+    };
+    const action = onCreated('100');
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+    expect(resultState.projectId).toBe('100');
+});
+
+test('onCreated with projectId type number shows project with id of type number', () => {
+    const initialState = {
+        projectId: null,
+        loadingState: LoadingState.CREATING_NEW
+    };
+    const action = onCreated(100);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+    expect(resultState.projectId).toBe(100);
+});
+
+test('onFetchedProjectData with id loads project data into vm', () => {
+    const initialState = {
+        projectData: null,
+        loadingState: LoadingState.FETCHING_WITH_ID
+    };
+    const action = onFetchedProjectData('1010101', initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_WITH_ID);
+    expect(resultState.projectData).toBe('1010101');
+});
+
+test('onFetchedProjectData new loads project data into vm', () => {
+    const initialState = {
+        projectData: null,
+        loadingState: LoadingState.FETCHING_NEW_DEFAULT
+    };
+    const action = onFetchedProjectData('1010101', initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_NEW_DEFAULT);
+    expect(resultState.projectData).toBe('1010101');
+});
+
+test('onFetchedProjectData new, to save loads project data into vm, prepares to save next', () => {
+    const initialState = {
+        projectData: null,
+        loadingState: LoadingState.FETCHING_NEW_DEFAULT_TO_SAVE
+    };
+    const action = onFetchedProjectData('1010101', initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_NEW_DEFAULT_TO_SAVE);
+    expect(resultState.projectData).toBe('1010101');
+});
+
+test('onLoadedProject upload shows without id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
+    };
+    const action = onLoadedProject(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
+});
+
+test('onLoadedProject with id shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_WITH_ID
+    };
+    const action = onLoadedProject(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onLoadedProject new shows without id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
+    };
+    const action = onLoadedProject(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
+});
+
+test('onLoadedProject new, to save shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_NEW_DEFAULT_TO_SAVE
+    };
+    const action = onLoadedProject(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onUpdated with id shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.SAVING_WITH_ID
+    };
+    const action = onUpdated(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onUpdated with id, before new, fetches default project', () => {
+    const initialState = {
+        loadingState: LoadingState.SAVING_WITH_ID_BEFORE_NEW
+    };
+    const action = onUpdated(initialState.loadingState);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT_TO_SAVE);
+});
+
+test('setProjectId, with same id as before, should show with id, not fetch', () => {
+    const initialState = {
+        projectId: '100',
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = setProjectId('100');
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+    expect(resultState.projectId).toBe('100');
+});
+
+test('setProjectId, with different id as before, should fetch with id, not show with id', () => {
+    const initialState = {
+        projectId: 99,
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = setProjectId('100');
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.FETCHING_WITH_ID);
+    expect(resultState.projectId).toBe('100');
+});
+
+test('setProjectId, with same id as before, but not same type, should fetch because not ===', () => {
+    const initialState = {
+        projectId: '100',
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = setProjectId(100);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.FETCHING_WITH_ID);
+    expect(resultState.projectId).toBe(100);
+});
+
+test('requestNewProject, when can\'t save, should fetch default project without id', () => {
+    const initialState = {
+        loadingState: LoadingState.SHOWING_WITHOUT_ID
+    };
+    const action = requestNewProject(false);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT);
+});
+
+test('requestNewProject, when can save, should save and prepare to fetch default project', () => {
+    const initialState = {
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = requestNewProject(true);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID_BEFORE_NEW);
+});
+
+test('onProjectUploadStarted when project not loaded should load', () => {
+    const initialState = {
+        loadingState: LoadingState.NOT_LOADED
+    };
+    const action = onProjectUploadStarted();
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
+});
+
+test('onProjectUploadStarted when showing project with id should load', () => {
+    const initialState = {
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = onProjectUploadStarted();
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
+});
+
+test('onProjectUploadStarted when showing project without id should load', () => {
+    const initialState = {
+        loadingState: LoadingState.SHOWING_WITHOUT_ID
+    };
+    const action = onProjectUploadStarted();
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
+});
+
+test('saveProject should prepare to save', () => {
+    const initialState = {
+        loadingState: LoadingState.SHOWING_WITH_ID
+    };
+    const action = saveProject();
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID);
+});
+
+test('onError from unloaded state should show error', () => {
+    const initialState = {
+        errStr: null,
+        loadingState: LoadingState.NOT_LOADED
+    };
+    const action = onError('Error string');
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.ERROR);
+    expect(resultState.errStr).toBe('Error string');
+});
+
+test('onError from showing project should show error', () => {
+    const initialState = {
+        errStr: null,
+        loadingState: LoadingState.FETCHING_WITH_ID
+    };
+    const action = onError('Error string');
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.ERROR);
+    expect(resultState.errStr).toBe('Error string');
+});
diff --git a/test/unit/util/hash-project-loader-hoc.test.jsx b/test/unit/util/hash-project-loader-hoc.test.jsx
index a78f43475f0d416ecfd2f48efbb5108b4872c3b4..208d1db16dfef950fbea90f38479b02f1b8cad1f 100644
--- a/test/unit/util/hash-project-loader-hoc.test.jsx
+++ b/test/unit/util/hash-project-loader-hoc.test.jsx
@@ -43,7 +43,7 @@ describe('HashParserHOC', () => {
                 store={store}
             />
         );
-        expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe(0);
+        expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe('0');
     });
 
     test('when the hash is not a number, it passes 0 as projectId', () => {
@@ -57,7 +57,7 @@ describe('HashParserHOC', () => {
                 store={store}
             />
         );
-        expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe(0);
+        expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe('0');
     });
 
     test('when hash change happens, the projectId state is changed', () => {