Fixed a search bug and added simple unit tests for the chapters API

This commit is contained in:
Alex Hunt 2018-05-18 19:37:03 -04:00
parent b8ce6ee29c
commit 3c5b93b3c2
12 changed files with 117 additions and 29 deletions

View File

@ -1,3 +1,8 @@
{ {
"presets": ["env", "react", "stage-3"] "presets": ["env", "react", "stage-3"],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
} }

View File

@ -15,6 +15,7 @@ UX:
SHORT LIST: SHORT LIST:
- More Responsive CSS - More Responsive CSS
- Does Get Document List Action Need State?
- Add Date Created / Updated Stamps, Introduce Sort Options - Add Date Created / Updated Stamps, Introduce Sort Options
- Testing - Testing
- Python Unit Tests - Python Unit Tests

View File

@ -1,5 +1,6 @@
from flask import Blueprint, render_template, abort, jsonify, request from flask import Blueprint, render_template, abort, jsonify, request
from elasticsearch import Elasticsearch from elasticsearch import Elasticsearch
import json
# Elasticsearch local connection # Elasticsearch local connection
# TODO: Extract to config # TODO: Extract to config
@ -239,4 +240,6 @@ def delete_tag(id):
''' Basic Text Search ''' ''' Basic Text Search '''
@api.route('/search/', methods=['POST']) @api.route('/search/', methods=['POST'])
def search_text(): 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)

9
jest.config.js Normal file
View File

@ -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)$': '<rootDir>/tests/__mocks__/fileMock.js',
'\\.(css|less)$': '<rootDir>/tests/__mocks__/styleMock.js'
}
};

View File

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"build": "webpack-cli --config webpack.prod.js --mode=production", "build": "webpack-cli --config webpack.prod.js --mode=production",
"watch": "webpack-cli --config webpack.dev.js --watch --mode=development", "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", "author": "Alex Hunt",
"license": "ISC", "license": "ISC",
@ -29,6 +29,7 @@
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-jest": "^22.4.3",
"babel-loader": "^7.1.2", "babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
@ -37,6 +38,7 @@
"css-loader": "^0.28.7", "css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11", "file-loader": "^1.1.11",
"jest": "^22.4.3",
"manifest-revision-webpack-plugin": "^0.4.1", "manifest-revision-webpack-plugin": "^0.4.1",
"node-sass": "^4.6.0", "node-sass": "^4.6.0",
"postcss-loader": "^2.0.8", "postcss-loader": "^2.0.8",

View File

@ -68,120 +68,120 @@ print 'Elasticsearch index created!'
# Sample data # Sample data
SAMPLE_DATA = [ SAMPLE_DATA = [
{'_op_type': 'index','_type': 'chapter', '_source': { {'_op_type': 'index','_type': 'chapter', '_id': 'AWNM3N3mxgFi4og697un', '_source': {
'number': 1, 'number': 1,
'title': 'Telemachus', 'title': 'Telemachus',
'html_source': get_chapter_text_from_seed_data('telem') '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, 'number': 2,
'title': 'Nestor', 'title': 'Nestor',
'html_source': get_chapter_text_from_seed_data('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, 'number': 3,
'title': 'Proteus', 'title': 'Proteus',
'html_source': get_chapter_text_from_seed_data('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, 'number': 4,
'title': 'Calyspo', 'title': 'Calyspo',
'html_source': get_chapter_text_from_seed_data('calypso') '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, 'number': 5,
'title': 'Lotus Eaters', 'title': 'Lotus Eaters',
'html_source': get_chapter_text_from_seed_data('lotus') '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, 'number': 6,
'title': 'Hades', 'title': 'Hades',
'html_source': get_chapter_text_from_seed_data('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, 'number': 7,
'title': 'Aeolus', 'title': 'Aeolus',
'html_source': get_chapter_text_from_seed_data('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, 'number': 8,
'title': 'Lestrygonians', 'title': 'Lestrygonians',
'html_source': get_chapter_text_from_seed_data('lestry') '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, 'number': 9,
'title': 'Scylla and Charybdis', 'title': 'Scylla and Charybdis',
'html_source': get_chapter_text_from_seed_data('scylla') '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, 'number': 10,
'title': 'Wandering Rocks', 'title': 'Wandering Rocks',
'html_source': get_chapter_text_from_seed_data('wrocks') '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, 'number': 11,
'title': 'Sirens', 'title': 'Sirens',
'html_source': get_chapter_text_from_seed_data('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, 'number': 12,
'title': 'Cyclops', 'title': 'Cyclops',
'html_source': get_chapter_text_from_seed_data('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, 'number': 13,
'title': 'Nausicaa', 'title': 'Nausicaa',
'html_source': get_chapter_text_from_seed_data('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, 'number': 14,
'title': 'Oxen of the Sun', 'title': 'Oxen of the Sun',
'html_source': get_chapter_text_from_seed_data('oxen') '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, 'number': 15,
'title': 'Circe', 'title': 'Circe',
'html_source': get_chapter_text_from_seed_data('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, 'number': 16,
'title': 'Eumaeus', 'title': 'Eumaeus',
'html_source': get_chapter_text_from_seed_data('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, 'number': 17,
'title': 'Ithaca', 'title': 'Ithaca',
'html_source': get_chapter_text_from_seed_data('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, 'number': 18,
'title': 'Penelope', 'title': 'Penelope',
'html_source': get_chapter_text_from_seed_data('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', 'title': 'Kinch',
'html_source': 'A knife' 'html_source': 'A knife'
}, },
}, },
{'_op_type': 'index', '_type': 'note', '_source': { {'_op_type': 'index', '_type': 'note','_id': 'AWNmqpdHxgFi4og697vS', '_source': {
'title': 'Lighthouse', 'title': 'Lighthouse',
'html_source': 'A lighthouse' 'html_source': 'A lighthouse'
}, },

View File

@ -8,7 +8,7 @@ const joyceAPI = store => next => action => {
switch(action.type) { switch(action.type) {
case 'GET_DOCUMENT_LIST': case 'GET_DOCUMENT_LIST':
if (action.status === 'request') { if (action.status === 'request') {
api.HTTPGetDocumentList(action.docType, action.state).then(response => api.HTTPGetDocumentList(action.docType).then(response =>
store.dispatch(actions.getDocumentList(response)) store.dispatch(actions.getDocumentList(response))
) )
} }

View File

@ -2,11 +2,12 @@ import axios from 'axios'
const apiRoute = '/api/' const apiRoute = '/api/'
const api = { const api = {
HTTPGetDocumentList: (docType, state) => HTTPGetDocumentList: (docType) =>
axios.get(apiRoute + docType).then(res => { 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 => { }).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) => HTTPGetDocumentText: (id, docType, state) =>
axios.get(apiRoute + docType + '/' + id).then(res => { axios.get(apiRoute + docType + '/' + id).then(res => {

View File

@ -23,8 +23,8 @@
.chapter_button > button { .chapter_button > button {
width: 100%; width: 100%;
margin: 0.25% 0; margin: 0.25% 0;
padding: 0.2rem 1rem; padding: 0.2rem 0;
font-size: 1rem; font-size: 1.5vw;
box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3); box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3);
} }

View File

@ -0,0 +1 @@
module.exports = 'test-file-stub';

View File

@ -0,0 +1 @@
module.exports = {};

65
tests/api.test.js Normal file
View File

@ -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: '<p>Ithaca!</p>'
}
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')
})
})