diff --git a/.babelrc b/.babelrc index 68bb865..89052ec 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,8 @@ { - "presets": ["env", "react", "stage-3"] + "presets": ["env", "react", "stage-3"], + "env": { + "test": { + "plugins": ["transform-es2015-modules-commonjs"] + } + } } \ No newline at end of file diff --git a/TODO.txt b/TODO.txt index 2df2589..bec392f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -15,6 +15,7 @@ UX: SHORT LIST: - More Responsive CSS + - Does Get Document List Action Need State? - Add Date Created / Updated Stamps, Introduce Sort Options - Testing - Python Unit Tests diff --git a/blueprints/api.py b/blueprints/api.py index a8fab61..ad52700 100644 --- a/blueprints/api.py +++ b/blueprints/api.py @@ -1,5 +1,6 @@ from flask import Blueprint, render_template, abort, jsonify, request from elasticsearch import Elasticsearch +import json # Elasticsearch local connection # TODO: Extract to config @@ -239,4 +240,6 @@ def delete_tag(id): ''' Basic Text Search ''' @api.route('/search/', methods=['POST']) def search_text(): - return jsonify(es_search_text(request.data['data'])) + data = json.loads(request.data) + results = es_search_text(data.get('data')) + return jsonify(results) diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..bb625f5 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + verbose: true, + testURL: 'http://localhost:5000', + testEnvironment: 'jsdom', + moduleNameMapper: { + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)$': '/tests/__mocks__/fileMock.js', + '\\.(css|less)$': '/tests/__mocks__/styleMock.js' + } +}; \ No newline at end of file diff --git a/package.json b/package.json index 02fd6a9..58f9a3d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "webpack-cli --config webpack.prod.js --mode=production", "watch": "webpack-cli --config webpack.dev.js --watch --mode=development", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "python setup.py && jest --config jest.config.js --no-cache && python setup.py" }, "author": "Alex Hunt", "license": "ISC", @@ -29,6 +29,7 @@ }, "devDependencies": { "babel-core": "^6.26.0", + "babel-jest": "^22.4.3", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", @@ -37,6 +38,7 @@ "css-loader": "^0.28.7", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.11", + "jest": "^22.4.3", "manifest-revision-webpack-plugin": "^0.4.1", "node-sass": "^4.6.0", "postcss-loader": "^2.0.8", diff --git a/setup.py b/setup.py index e1587c0..4f303d7 100644 --- a/setup.py +++ b/setup.py @@ -68,120 +68,120 @@ print 'Elasticsearch index created!' # Sample data SAMPLE_DATA = [ - {'_op_type': 'index','_type': 'chapter', '_source': { + {'_op_type': 'index','_type': 'chapter', '_id': 'AWNM3N3mxgFi4og697un', '_source': { 'number': 1, 'title': 'Telemachus', 'html_source': get_chapter_text_from_seed_data('telem') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vA', '_source': { 'number': 2, 'title': 'Nestor', 'html_source': get_chapter_text_from_seed_data('nestor') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vB', '_source': { 'number': 3, 'title': 'Proteus', 'html_source': get_chapter_text_from_seed_data('proteus') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vC', '_source': { 'number': 4, 'title': 'Calyspo', 'html_source': get_chapter_text_from_seed_data('calypso') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vD', '_source': { 'number': 5, 'title': 'Lotus Eaters', 'html_source': get_chapter_text_from_seed_data('lotus') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vE', '_source': { 'number': 6, 'title': 'Hades', 'html_source': get_chapter_text_from_seed_data('hades') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vF', '_source': { 'number': 7, 'title': 'Aeolus', 'html_source': get_chapter_text_from_seed_data('aeolus') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vG', '_source': { 'number': 8, 'title': 'Lestrygonians', 'html_source': get_chapter_text_from_seed_data('lestry') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vH', '_source': { 'number': 9, 'title': 'Scylla and Charybdis', 'html_source': get_chapter_text_from_seed_data('scylla') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vI', '_source': { 'number': 10, 'title': 'Wandering Rocks', 'html_source': get_chapter_text_from_seed_data('wrocks') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vJ', '_source': { 'number': 11, 'title': 'Sirens', 'html_source': get_chapter_text_from_seed_data('sirens') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vK', '_source': { 'number': 12, 'title': 'Cyclops', 'html_source': get_chapter_text_from_seed_data('cyclops') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vL', '_source': { 'number': 13, 'title': 'Nausicaa', 'html_source': get_chapter_text_from_seed_data('nausicaa') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vM', '_source': { 'number': 14, 'title': 'Oxen of the Sun', 'html_source': get_chapter_text_from_seed_data('oxen') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vN', '_source': { 'number': 15, 'title': 'Circe', 'html_source': get_chapter_text_from_seed_data('circe') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vO', '_source': { 'number': 16, 'title': 'Eumaeus', 'html_source': get_chapter_text_from_seed_data('eumaeus') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vP', '_source': { 'number': 17, 'title': 'Ithaca', 'html_source': get_chapter_text_from_seed_data('ithaca') }, }, - {'_op_type': 'index', '_type': 'chapter', '_source': { + {'_op_type': 'index', '_type': 'chapter','_id': 'AWNmqpdHxgFi4og697vQ', '_source': { 'number': 18, 'title': 'Penelope', 'html_source': get_chapter_text_from_seed_data('penelope') }, }, - {'_op_type': 'index', '_type': 'note', '_source': { + {'_op_type': 'index', '_type': 'note','_id': 'AWNmqpdHxgFi4og697vR', '_source': { 'title': 'Kinch', 'html_source': 'A knife' }, }, - {'_op_type': 'index', '_type': 'note', '_source': { + {'_op_type': 'index', '_type': 'note','_id': 'AWNmqpdHxgFi4og697vS', '_source': { 'title': 'Lighthouse', 'html_source': 'A lighthouse' }, diff --git a/src/middleware/joyceAPI.js b/src/middleware/joyceAPI.js index a2464ce..4f9755e 100644 --- a/src/middleware/joyceAPI.js +++ b/src/middleware/joyceAPI.js @@ -8,7 +8,7 @@ const joyceAPI = store => next => action => { switch(action.type) { case 'GET_DOCUMENT_LIST': if (action.status === 'request') { - api.HTTPGetDocumentList(action.docType, action.state).then(response => + api.HTTPGetDocumentList(action.docType).then(response => store.dispatch(actions.getDocumentList(response)) ) } diff --git a/src/modules/api.js b/src/modules/api.js index 66497e8..6ed7f5d 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -2,11 +2,12 @@ import axios from 'axios' const apiRoute = '/api/' const api = { - HTTPGetDocumentList: (docType, state) => + HTTPGetDocumentList: (docType) => axios.get(apiRoute + docType).then(res => { - return {status: 'success', docType: docType, state: state, data: res.data} + return {status: 'success', docType: docType, data: res.data} }).catch(error => { - return {status: 'error', docType: docType, state: state, data: error} + console.log(error) + return {status: 'error', docType: docType, data: error} }), HTTPGetDocumentText: (id, docType, state) => axios.get(apiRoute + docType + '/' + id).then(res => { diff --git a/src/stylesheets/_sidebar.scss b/src/stylesheets/_sidebar.scss index 3ea126d..3d24759 100644 --- a/src/stylesheets/_sidebar.scss +++ b/src/stylesheets/_sidebar.scss @@ -23,8 +23,8 @@ .chapter_button > button { width: 100%; margin: 0.25% 0; - padding: 0.2rem 1rem; - font-size: 1rem; + padding: 0.2rem 0; + font-size: 1.5vw; box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3); } diff --git a/tests/__mocks__/fileMock.js b/tests/__mocks__/fileMock.js new file mode 100644 index 0000000..84c1da6 --- /dev/null +++ b/tests/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; \ No newline at end of file diff --git a/tests/__mocks__/styleMock.js b/tests/__mocks__/styleMock.js new file mode 100644 index 0000000..a099545 --- /dev/null +++ b/tests/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; \ No newline at end of file diff --git a/tests/api.test.js b/tests/api.test.js new file mode 100644 index 0000000..f85b36c --- /dev/null +++ b/tests/api.test.js @@ -0,0 +1,65 @@ +const api = require('../src/modules/api') + +// beforeEach(() => { + +// }) + +// Chapters + +test('API returned chapter list', () => { + expect.assertions(3) + return api.default.HTTPGetDocumentList('chapters').then(response => { + expect(response.status).toBe('success') + expect(response.docType).toBe('chapters') + expect(response.data.length).toBe(18) + }) +}) + +test('API returned chapter document', () => { + expect.assertions(5) + return api.default.HTTPGetDocumentText('AWNM3N3mxgFi4og697un', 'chapters').then(response => { + expect(response.status).toBe('success') + expect(response.docType).toBe('chapters') + expect(response.data.title).toBe('Telemachus') + expect(response.data.number).toBe(1) + expect(response.data.html_source).toBeDefined() + }) +}) + +test('API successfully edited document', () => { + expect.assertions(3) + const updatedChapter = { + number: 1, + title: 'Odyssey', + html_source: 'No more text.' + } + return api.default.HTTPPostWriteDocument('AWNM3N3mxgFi4og697un', 'chapters', updatedChapter).then(response => { + console.log(response.data) + expect(response.status).toBe('success') + expect(response.data.length).toBe(18) + expect(response.data[0].title).toBe('Odyssey') + }) +}) + +test('API successfully created document', () => { + expect.assertions(3) + const newChapter = { + title: 'Test', + number: 19, + html_source: '

Ithaca!

' + } + return api.default.HTTPPutCreateDocument('chapters', newChapter).then(response => { + expect(response.status).toBe('success') + expect(response.data.length).toBe(19) + expect(response.data.slice(-1)[0].title).toBe('Test') + }) +}) + +test('API successfully deleted document', () => { + expect.assertions(3) + return api.default.HTTPDeleteDocument('AWNM3N3mxgFi4og697un', 'chapters').then(response => { + expect(response.status).toBe('success') + expect(response.data.length).toBe(18) + expect(response.data[0].title).toBe('Nestor') + }) +}) \ No newline at end of file