Skip to content

Commit e2a280e

Browse files
authored
Merge pull request #3360 from benjiwheeler/project-state-reducer-tests
project state reducer tests, bug fixes
2 parents 7e442bc + dc9edbf commit e2a280e

File tree

7 files changed

+257
-9
lines changed

7 files changed

+257
-9
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ To run unit tests in watch mode (watches for code changes and continuously runs
8686
npm run test:unit -- --watch
8787
```
8888

89+
You can run a single file of integration tests (in this example, the `button` tests):
90+
91+
```bash
92+
$(npm bin)/jest --runInBand test/unit/components/button.test.jsx
93+
```
94+
8995
#### Running integration tests
9096

9197
Integration tests use a headless browser to manipulate the actual html and javascript that the repo

src/lib/hash-parser-hoc.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const HashParserHOC = function (WrappedComponent) {
4242
handleHashChange () {
4343
const hashMatch = window.location.hash.match(/#(\d+)/);
4444
const hashProjectId = hashMatch === null ? defaultProjectId : hashMatch[1];
45-
this.props.setProjectId(hashProjectId);
45+
this.props.setProjectId(hashProjectId.toString());
4646
if (hashProjectId !== defaultProjectId) {
4747
this.setState({hideIntro: true});
4848
}

src/lib/project-fetcher-hoc.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
4040
props.projectId !== null &&
4141
typeof props.projectId !== 'undefined'
4242
) {
43-
this.props.setProjectId(props.projectId);
43+
this.props.setProjectId(props.projectId.toString());
4444
}
4545
}
4646
componentDidUpdate (prevProps) {

src/lib/project-saver-hoc.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
3939
if (this.props.isCreating && !prevProps.isCreating) {
4040
this.storeProject()
4141
.then(response => {
42-
this.props.onCreated(response.id);
42+
this.props.onCreated(response.id.toString());
4343
})
4444
.catch(err => {
4545
// NOTE: should throw up a notice for user

src/reducers/project-state.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const START_LOADING_VM_FILE_UPLOAD = 'scratch-gui/project-state/START_LOADING_FI
1717
const START_SAVING = 'scratch-gui/project-state/START_SAVING';
1818
const START_SAVING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_SAVING_BEFORE_CREATING_NEW';
1919

20-
const defaultProjectId = 0; // hardcoded id of default project
20+
const defaultProjectId = '0'; // hardcoded id of default project
2121

2222
const LoadingState = keyMirror({
2323
NOT_LOADED: null,
@@ -83,7 +83,7 @@ const reducer = function (state, action) {
8383
if (state.loadingState === LoadingState.CREATING_NEW) {
8484
return Object.assign({}, state, {
8585
loadingState: LoadingState.SHOWING_WITH_ID,
86-
id: action.id
86+
projectId: action.id
8787
});
8888
}
8989
return state;
@@ -160,8 +160,8 @@ const reducer = function (state, action) {
160160
}
161161
return state;
162162
case SET_PROJECT_ID:
163-
// if we were already showing something, only fetch project if the
164-
// project id has changed. This prevents re-fetching projects unnecessarily.
163+
// if we were already showing a project, and a different projectId is set, only fetch that project if
164+
// projectId has changed. This prevents re-fetching projects unnecessarily.
165165
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
166166
if (state.projectId !== action.id) {
167167
return Object.assign({}, state, {
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/* eslint-env jest */
2+
import projectStateReducer from '../../../src/reducers/project-state';
3+
import {
4+
LoadingState,
5+
onCreated,
6+
onError,
7+
onFetchedProjectData,
8+
onLoadedProject,
9+
onProjectUploadStarted,
10+
onUpdated,
11+
requestNewProject,
12+
saveProject,
13+
setProjectId
14+
} from '../../../src/reducers/project-state';
15+
16+
test('initialState', () => {
17+
let defaultState;
18+
/* projectStateReducer(state, action) */
19+
expect(projectStateReducer(defaultState, {type: 'anything'})).toBeDefined();
20+
expect(projectStateReducer(defaultState, {type: 'anything'}).errStr).toBe(null);
21+
expect(projectStateReducer(defaultState, {type: 'anything'}).projectData).toBe(null);
22+
expect(projectStateReducer(defaultState, {type: 'anything'}).projectId).toBe(null);
23+
expect(projectStateReducer(defaultState, {type: 'anything'}).loadingState).toBe(LoadingState.NOT_LOADED);
24+
});
25+
26+
test('onCreated with projectId type string shows project with that id', () => {
27+
const initialState = {
28+
projectId: null,
29+
loadingState: LoadingState.CREATING_NEW
30+
};
31+
const action = onCreated('100');
32+
const resultState = projectStateReducer(initialState, action);
33+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
34+
expect(resultState.projectId).toBe('100');
35+
});
36+
37+
test('onCreated with projectId type number shows project with id of type number', () => {
38+
const initialState = {
39+
projectId: null,
40+
loadingState: LoadingState.CREATING_NEW
41+
};
42+
const action = onCreated(100);
43+
const resultState = projectStateReducer(initialState, action);
44+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
45+
expect(resultState.projectId).toBe(100);
46+
});
47+
48+
test('onFetchedProjectData with id loads project data into vm', () => {
49+
const initialState = {
50+
projectData: null,
51+
loadingState: LoadingState.FETCHING_WITH_ID
52+
};
53+
const action = onFetchedProjectData('1010101', initialState.loadingState);
54+
const resultState = projectStateReducer(initialState, action);
55+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_WITH_ID);
56+
expect(resultState.projectData).toBe('1010101');
57+
});
58+
59+
test('onFetchedProjectData new loads project data into vm', () => {
60+
const initialState = {
61+
projectData: null,
62+
loadingState: LoadingState.FETCHING_NEW_DEFAULT
63+
};
64+
const action = onFetchedProjectData('1010101', initialState.loadingState);
65+
const resultState = projectStateReducer(initialState, action);
66+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_NEW_DEFAULT);
67+
expect(resultState.projectData).toBe('1010101');
68+
});
69+
70+
test('onFetchedProjectData new, to save loads project data into vm, prepares to save next', () => {
71+
const initialState = {
72+
projectData: null,
73+
loadingState: LoadingState.FETCHING_NEW_DEFAULT_TO_SAVE
74+
};
75+
const action = onFetchedProjectData('1010101', initialState.loadingState);
76+
const resultState = projectStateReducer(initialState, action);
77+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_NEW_DEFAULT_TO_SAVE);
78+
expect(resultState.projectData).toBe('1010101');
79+
});
80+
81+
test('onLoadedProject upload shows without id', () => {
82+
const initialState = {
83+
loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
84+
};
85+
const action = onLoadedProject(initialState.loadingState);
86+
const resultState = projectStateReducer(initialState, action);
87+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
88+
});
89+
90+
test('onLoadedProject with id shows with id', () => {
91+
const initialState = {
92+
loadingState: LoadingState.LOADING_VM_WITH_ID
93+
};
94+
const action = onLoadedProject(initialState.loadingState);
95+
const resultState = projectStateReducer(initialState, action);
96+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
97+
});
98+
99+
test('onLoadedProject new shows without id', () => {
100+
const initialState = {
101+
loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
102+
};
103+
const action = onLoadedProject(initialState.loadingState);
104+
const resultState = projectStateReducer(initialState, action);
105+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
106+
});
107+
108+
test('onLoadedProject new, to save shows with id', () => {
109+
const initialState = {
110+
loadingState: LoadingState.LOADING_VM_NEW_DEFAULT_TO_SAVE
111+
};
112+
const action = onLoadedProject(initialState.loadingState);
113+
const resultState = projectStateReducer(initialState, action);
114+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
115+
});
116+
117+
test('onUpdated with id shows with id', () => {
118+
const initialState = {
119+
loadingState: LoadingState.SAVING_WITH_ID
120+
};
121+
const action = onUpdated(initialState.loadingState);
122+
const resultState = projectStateReducer(initialState, action);
123+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
124+
});
125+
126+
test('onUpdated with id, before new, fetches default project', () => {
127+
const initialState = {
128+
loadingState: LoadingState.SAVING_WITH_ID_BEFORE_NEW
129+
};
130+
const action = onUpdated(initialState.loadingState);
131+
const resultState = projectStateReducer(initialState, action);
132+
expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT_TO_SAVE);
133+
});
134+
135+
test('setProjectId, with same id as before, should show with id, not fetch', () => {
136+
const initialState = {
137+
projectId: '100',
138+
loadingState: LoadingState.SHOWING_WITH_ID
139+
};
140+
const action = setProjectId('100');
141+
const resultState = projectStateReducer(initialState, action);
142+
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
143+
expect(resultState.projectId).toBe('100');
144+
});
145+
146+
test('setProjectId, with different id as before, should fetch with id, not show with id', () => {
147+
const initialState = {
148+
projectId: 99,
149+
loadingState: LoadingState.SHOWING_WITH_ID
150+
};
151+
const action = setProjectId('100');
152+
const resultState = projectStateReducer(initialState, action);
153+
expect(resultState.loadingState).toBe(LoadingState.FETCHING_WITH_ID);
154+
expect(resultState.projectId).toBe('100');
155+
});
156+
157+
test('setProjectId, with same id as before, but not same type, should fetch because not ===', () => {
158+
const initialState = {
159+
projectId: '100',
160+
loadingState: LoadingState.SHOWING_WITH_ID
161+
};
162+
const action = setProjectId(100);
163+
const resultState = projectStateReducer(initialState, action);
164+
expect(resultState.loadingState).toBe(LoadingState.FETCHING_WITH_ID);
165+
expect(resultState.projectId).toBe(100);
166+
});
167+
168+
test('requestNewProject, when can\'t save, should fetch default project without id', () => {
169+
const initialState = {
170+
loadingState: LoadingState.SHOWING_WITHOUT_ID
171+
};
172+
const action = requestNewProject(false);
173+
const resultState = projectStateReducer(initialState, action);
174+
expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT);
175+
});
176+
177+
test('requestNewProject, when can save, should save and prepare to fetch default project', () => {
178+
const initialState = {
179+
loadingState: LoadingState.SHOWING_WITH_ID
180+
};
181+
const action = requestNewProject(true);
182+
const resultState = projectStateReducer(initialState, action);
183+
expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID_BEFORE_NEW);
184+
});
185+
186+
test('onProjectUploadStarted when project not loaded should load', () => {
187+
const initialState = {
188+
loadingState: LoadingState.NOT_LOADED
189+
};
190+
const action = onProjectUploadStarted();
191+
const resultState = projectStateReducer(initialState, action);
192+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
193+
});
194+
195+
test('onProjectUploadStarted when showing project with id should load', () => {
196+
const initialState = {
197+
loadingState: LoadingState.SHOWING_WITH_ID
198+
};
199+
const action = onProjectUploadStarted();
200+
const resultState = projectStateReducer(initialState, action);
201+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
202+
});
203+
204+
test('onProjectUploadStarted when showing project without id should load', () => {
205+
const initialState = {
206+
loadingState: LoadingState.SHOWING_WITHOUT_ID
207+
};
208+
const action = onProjectUploadStarted();
209+
const resultState = projectStateReducer(initialState, action);
210+
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
211+
});
212+
213+
test('saveProject should prepare to save', () => {
214+
const initialState = {
215+
loadingState: LoadingState.SHOWING_WITH_ID
216+
};
217+
const action = saveProject();
218+
const resultState = projectStateReducer(initialState, action);
219+
expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID);
220+
});
221+
222+
test('onError from unloaded state should show error', () => {
223+
const initialState = {
224+
errStr: null,
225+
loadingState: LoadingState.NOT_LOADED
226+
};
227+
const action = onError('Error string');
228+
const resultState = projectStateReducer(initialState, action);
229+
expect(resultState.loadingState).toBe(LoadingState.ERROR);
230+
expect(resultState.errStr).toBe('Error string');
231+
});
232+
233+
test('onError from showing project should show error', () => {
234+
const initialState = {
235+
errStr: null,
236+
loadingState: LoadingState.FETCHING_WITH_ID
237+
};
238+
const action = onError('Error string');
239+
const resultState = projectStateReducer(initialState, action);
240+
expect(resultState.loadingState).toBe(LoadingState.ERROR);
241+
expect(resultState.errStr).toBe('Error string');
242+
});

test/unit/util/hash-project-loader-hoc.test.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('HashParserHOC', () => {
4343
store={store}
4444
/>
4545
);
46-
expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe(0);
46+
expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe('0');
4747
});
4848

4949
test('when the hash is not a number, it passes 0 as projectId', () => {
@@ -57,7 +57,7 @@ describe('HashParserHOC', () => {
5757
store={store}
5858
/>
5959
);
60-
expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe(0);
60+
expect(mockSetProjectIdFunc.mock.calls[0][0]).toBe('0');
6161
});
6262

6363
test('when hash change happens, the projectId state is changed', () => {

0 commit comments

Comments
 (0)