Skip to content

Commit f628b85

Browse files
committed
CODE: Refactor and spawn into a separate library TC-related actions/reducers/services
Submission 253940 by liuliquan to the challenge https://www.topcoder.com/challenges/30064737
0 parents  commit f628b85

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+36195
-0
lines changed

Diff for: .babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["topcoder-react-utils/config/babel/node-ssr"]
3+
}

Diff for: .circleci/config.yml

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
version: 2
2+
jobs:
3+
# Release.
4+
# TODO: Probably, it is possible to reuse the test job below as a part of
5+
# the release job; but as I don't know how to do it at the moment, let's
6+
# just dublicate the logic for now.
7+
release:
8+
docker:
9+
- image: circleci/node:8.9.4
10+
steps:
11+
- checkout
12+
- restore_cache:
13+
# Careful here, though, at the moment, there should be no problem
14+
# to reuse the same cache of "node_modules" between test and release
15+
# jobs.
16+
key: test-node-modules-{{ checksum "package-lock.json" }}
17+
- run: npm install
18+
- save_cache:
19+
key: test-node-modules-{{ checksum "package-lock.json" }}
20+
paths:
21+
- node_modules
22+
- run: npm test
23+
- run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
24+
- run: npm publish
25+
26+
# Just tests commited code.
27+
test:
28+
docker:
29+
- image: circleci/node:8.9.4
30+
steps:
31+
- checkout
32+
- restore_cache:
33+
key: test-node-modules-{{ checksum "package-lock.json" }}
34+
- run: npm install
35+
- save_cache:
36+
key: test-node-modules-{{ checksum "package-lock.json" }}
37+
paths:
38+
- node_modules
39+
- run: npm test
40+
41+
workflows:
42+
version: 2
43+
build:
44+
jobs:
45+
- release:
46+
filters:
47+
branches:
48+
only: master
49+
- test:
50+
filters:
51+
branches:
52+
ignore: master
53+

Diff for: .eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__coverage__
2+
dist
3+
node_modules

Diff for: .eslintrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "./node_modules/topcoder-react-utils/config/eslint/default.json"
3+
}

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__coverage__
2+
.build-info
3+
.sass-cache
4+
dist
5+
node_modules

Diff for: .nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v8.11.1

Diff for: .stylelintrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "topcoder-react-utils/config/stylelint/default"
3+
}

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Topcoder React Lib
2+
3+
### v0.0.x
4+
Pre-release drafts of the initial library version. A big journey starts here.

Diff for: README.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Topcoder React Lib
2+
3+
The [Topcoder](https://www.topcoder.com) lib for internal ReactJS projects.
4+
5+
### Development
6+
```shell
7+
# Install dependencies
8+
npm install
9+
10+
# Run test & build
11+
npm test
12+
npm run build
13+
14+
# Go to other project which depends on the topcoder-react-lib, config its package.json so
15+
# that the 'topcoder-react-lib' points to the local foler path of topcoder-react-lib:
16+
#
17+
# "dependencies": {
18+
# "topcoder-react-lib": "<local-path-to-topcoder-react-lib>",
19+
# ......
20+
# }
21+
#
22+
```
23+
24+
### License
25+
UNLICENSED &mdash; for internal Topcoder projects only.

Diff for: __tests__/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["topcoder-react-utils/config/babel/node-ssr"]
3+
}

Diff for: __tests__/.eslintrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../node_modules/topcoder-react-utils/config/eslint/jest.json"
3+
}

Diff for: __tests__/actions/auth.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
jest.setMock('isomorphic-fetch', {});
2+
3+
const GROUPS_REQ_URL =
4+
'https://api.topcoder-dev.com/v3/groups?memberId=12345&membershipType=user';
5+
const PROFILE_REQ_URL = 'https://api.topcoder-dev.com/v3/members/username12345';
6+
7+
global.fetch = jest.fn(url => Promise.resolve({
8+
json: () => {
9+
let content;
10+
switch (url) {
11+
case GROUPS_REQ_URL: content = ['Group1', 'Group2']; break;
12+
case PROFILE_REQ_URL: content = { userId: 12345 }; break;
13+
default: throw new Error('Unexpected URL!');
14+
}
15+
return {
16+
result: { content, status: 200 },
17+
};
18+
},
19+
}));
20+
21+
const { actions } = require('../../src');
22+
23+
describe('fetch with success response', () => {
24+
beforeEach(() => jest.clearAllMocks());
25+
26+
test('auth.loadProfile works as expected when authenticated', () => {
27+
const action = actions.auth.loadProfile('token');
28+
expect(action.type).toBe('AUTH/LOAD_PROFILE');
29+
return action.payload.then((res) => {
30+
expect(global.fetch).toHaveBeenCalledWith(PROFILE_REQ_URL, {
31+
headers: {
32+
Authorization: 'Bearer token',
33+
'Content-Type': 'application/json',
34+
},
35+
});
36+
expect(global.fetch).toHaveBeenCalledWith(GROUPS_REQ_URL, {
37+
headers: {
38+
Authorization: 'Bearer token',
39+
'Content-Type': 'application/json',
40+
},
41+
});
42+
expect(res).toEqual({
43+
groups: ['Group1', 'Group2'],
44+
userId: 12345,
45+
});
46+
});
47+
});
48+
49+
test('auth.loadProfile with empty token', () => {
50+
const action = actions.auth.loadProfile('');
51+
expect(action.type).toBe('AUTH/LOAD_PROFILE');
52+
return action.payload.then((res) => {
53+
expect(res).toBe(null);
54+
});
55+
});
56+
});
57+
58+
describe('fetch with failed response', () => {
59+
beforeAll(() => {
60+
global.fetch = jest.fn(() => Promise.resolve({
61+
json: () => ({
62+
result: { status: 404 },
63+
}),
64+
}));
65+
});
66+
67+
test('fetch return 404', () => {
68+
const action = actions.auth.loadProfile('token');
69+
expect(action.type).toBe('AUTH/LOAD_PROFILE');
70+
return action.payload.then((res) => {
71+
expect(res).toEqual({
72+
groups: [],
73+
});
74+
});
75+
});
76+
});

Diff for: __tests__/actions/challenge.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { actions } from '../../src';
2+
3+
jest.mock('../../src/services/challenges');
4+
5+
const mockFetch = resolvesTo => jest.fn(() =>
6+
Promise.resolve({ json: () => resolvesTo }));
7+
8+
let originalFetch;
9+
10+
beforeAll(() => {
11+
originalFetch = global.fetch;
12+
});
13+
14+
afterAll(() => {
15+
global.fetch = originalFetch;
16+
});
17+
18+
describe('challenge.fetchChallengeInit', () => {
19+
const a = actions.challenge.getDetailsInit(12345);
20+
21+
test('has expected type', () => {
22+
expect(a.type).toBe('CHALLENGE/GET_DETAILS_INIT');
23+
});
24+
25+
test('payload is the challenge ID, converted to string, if necessary', () =>
26+
expect(a.payload).toBe('12345'));
27+
});
28+
29+
describe('challenge.fetchSubmissionsInit', () => {
30+
const a = actions.challenge.getSubmissionsInit(12345);
31+
32+
test('has expected type', () => {
33+
expect(a.type).toBe('CHALLENGE/GET_SUBMISSIONS_INIT');
34+
});
35+
36+
test('payload is challengeId', () =>
37+
expect(a.payload).toBe('12345'));
38+
});
39+
40+
describe('challenge.getDetailsDone', () => {
41+
global.fetch = mockFetch({ result: { content: ['DUMMY DATA'] } });
42+
43+
const a = actions.challenge.getDetailsDone(12345);
44+
45+
test('has expected type', () => {
46+
expect(a.type).toBe('CHALLENGE/GET_DETAILS_DONE');
47+
});
48+
49+
const mockChallenge =
50+
require('../../src/services/__mocks__/data/challenge-normalized.json');
51+
52+
test('payload is a promise which resolves to the expected object', () =>
53+
a.payload.then((res) => {
54+
res.communities = Array.from(res.communities);
55+
expect(res).toEqual(mockChallenge);
56+
}));
57+
});
58+
59+
describe('challenge.fetchSubmissionsDone', () => {
60+
global.fetch = mockFetch({
61+
challengeId: '12345',
62+
submissions: 'DUMMY DATA',
63+
});
64+
65+
const a = actions.challenge.getSubmissionsDone(12345, {});
66+
67+
test('has expected type', () => {
68+
expect(a.type).toBe('CHALLENGE/GET_SUBMISSIONS_DONE');
69+
});
70+
71+
test('payload is a promise which resolves to the expected object', () =>
72+
a.payload.then(res => expect(res).toEqual({
73+
challengeId: '12345',
74+
submissions: 'DUMMY DATA',
75+
})));
76+
});

Diff for: __tests__/actions/smp.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { actions } from '../../src';
2+
3+
let originalFetch;
4+
5+
beforeAll(() => {
6+
originalFetch = global.fetch;
7+
});
8+
9+
afterAll(() => {
10+
global.fetch = originalFetch;
11+
});
12+
13+
describe('smp.deleteSubmissionDone', () => {
14+
global.fetch = jest.fn(() => Promise.resolve());
15+
16+
const a = actions.smp.deleteSubmissionDone('Token V3', 'submissionId');
17+
18+
test('has expected type', () => {
19+
expect(a.type).toBe('SMP/DELETE_SUBMISSION_DONE');
20+
});
21+
22+
test('Calls the correct endpoint', () => {
23+
expect(global.fetch).toHaveBeenCalledWith('https://api.topcoder-dev.com/v3/submissions/submissionId', {
24+
headers: {
25+
Authorization: 'Bearer Token V3',
26+
'Content-Type': 'application/json',
27+
},
28+
method: 'DELETE',
29+
});
30+
});
31+
32+
test('payload be submissionId', () =>
33+
a.payload.then(res => expect(res).toEqual('submissionId')));
34+
});
35+
36+
describe('smp.downloadSubmission', () => {
37+
test('does not throw', () => {
38+
expect(() =>
39+
actions.smp.downloadSubmission({}, 'design', '12345')).not.toThrow();
40+
});
41+
});

0 commit comments

Comments
 (0)