diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..7a73a41
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/exercises/12_spa/books/__tests__/books.test.js b/exercises/12_spa/books/__tests__/books.test.js
index f593373..7e643d7 100644
--- a/exercises/12_spa/books/__tests__/books.test.js
+++ b/exercises/12_spa/books/__tests__/books.test.js
@@ -8,9 +8,7 @@ const books = require('../modules/books')
jest.mock('../modules/google')
-//console.log(__dirname)
-
-let goodData
+const path = './modules/__mocks__/__mockData__/'
describe('searchGoogle', () => {
@@ -20,12 +18,14 @@ describe('searchGoogle', () => {
//console.log(data)
return data
})
+
})
describe('extractFields', () => {
+ let goodData
+
beforeAll( () => {
- const path = './modules/__mocks__/__mockData__/'
goodData = fs.readFileSync(`${path}java.json`, 'utf8')
expect(typeof goodData).toBe('string')
})
@@ -33,20 +33,96 @@ describe('extractFields', () => {
test('extracted data is in an array', () => {
const bookData = books.extractFields(goodData)
expect(Array.isArray(bookData)).toBeTruthy()
- expect(bookData.length).toBe(2)
+ expect(bookData.length).toBe(20)
+ })
+
+ test('passing string without books array', () => {
+ expect(() => books.extractFields('{"name": "Mark"}'))
+ .toThrowError('no book data found')
+ })
+
+ test('passing object instead of string', () => {
+ expect(() => books.extractFields({name: 'Mark'}))
+ .toThrowError('parameter has invalid data type')
})
test('extract title fields', () => {
const bookData = books.extractFields(goodData)
- console.log(bookData)
- expect(bookData[0].title).toBe('Java Book One')
- expect(bookData[1].title).toBe('Java Book Two')
+ expect(bookData[0].title).toBe('Thinking in Java')
+ expect(bookData[1].title).toBe('Practical Java')
})
test('extract ISBN13 data', () => {
const bookData = books.extractFields(goodData)
- expect(bookData[0].isbn).toBe(9780201616460)
- expect(bookData[1].isbn).toBe(9789793780146)
+ expect(bookData[0].isbn).toBe(9780131002876)
+ expect(bookData[1].isbn).toBe(9780201616460)
+ })
+
+})
+
+describe('build table string', () => {
+
+ let goodData
+ let goodHTML
+
+ beforeAll( () => {
+ goodData = JSON.parse(fs.readFileSync(`${path}extractedData.json`, 'utf8'))
+ goodHTML = fs.readFileSync(`${path}validTable.txt`, 'utf8')
+ })
+
+ test('check parameter is an object', () => {
+ expect(typeof goodData).toBe('object')
+ })
+
+ test('thow error if parameter is not an object', () => {
+ expect(() => books.buildTable('bad parameter'))
+ .toThrowError('invalid parameter data type')
+ })
+
+ test('check that parameter is an array (not object)', () => {
+ expect(() => books.buildTable({name: 'Mark'}))
+ .toThrowError('invalid parameter data type')
+ })
+
+ test('check the function returns a string', () => {
+ const table = books.buildTable(goodData)
+ expect(typeof table).toBe('string')
+ })
+
+ test('build 2 column table', async() => {
+ const table = books.buildTable(goodData)
+ // compare text using regex to remove whitespace
+ expect(table.replace(/\s/g, '')).toBe(goodHTML.replace(/\s/g, ''))
+ })
+
+})
+
+describe('search', () => {
+
+ let req
+
+ beforeEach( () => {
+ req = JSON.parse(fs.readFileSync(`${path}req.json`, 'utf8'))
+ })
+
+ test('make a search with valid request', async() => {
+ const result = await books.search(req)
+ const expected = fs.readFileSync(`${path}validTable.txt`, 'utf8')
+ // compare text using regex to remove whitespace
+ expect(result.replace(/\s/g, '')).toBe(expected.replace(/\s/g, ''))
+ })
+
+ test('make a search with no query parameters', async() => {
+ delete req.query
+ const result = await books.search(req)
+ expect(result.replace(/\s/g, '')).toBe('')
+ })
+
+ test('make a search with missing q query parameter', async() => {
+ req.query.r = 'something'
+ delete req.query.q
+ const result = await books.search(req)
+ expect(result.replace(/\s/g, '')).toBe('')
})
})
diff --git a/exercises/12_spa/books/__tests__/google.test.js b/exercises/12_spa/books/__tests__/google.test.js
index 956cddf..c8d10fc 100644
--- a/exercises/12_spa/books/__tests__/google.test.js
+++ b/exercises/12_spa/books/__tests__/google.test.js
@@ -13,7 +13,7 @@ describe('searchString', () => {
expect(typeof data).toBe('string')
const json = JSON.parse(data)
expect(Array.isArray(json.items)).toBeTruthy()
- expect(json.items.length).toBe(2)
+ expect(json.items.length).toBe(20)
})
})
diff --git a/exercises/12_spa/books/html/index.html b/exercises/12_spa/books/html/index.html
index 2e4b7b5..9209f66 100644
--- a/exercises/12_spa/books/html/index.html
+++ b/exercises/12_spa/books/html/index.html
@@ -4,11 +4,15 @@
- The HTML5 Template
-
-
+ Google Book Search
+
+
-
+
+ ${books}
diff --git a/exercises/12_spa/books/index.js b/exercises/12_spa/books/index.js
index 08f22c4..ae53933 100644
--- a/exercises/12_spa/books/index.js
+++ b/exercises/12_spa/books/index.js
@@ -27,10 +27,10 @@ const port = 8080
app.get('/', async(req, res) => {
try {
+ console.log(`query ${req.query.q}`)
const bookList = await books.search(req)
- console.log(typeof bookList)
console.log(bookList)
- res.render('index', {locals: {books: books}})
+ res.render('index', {locals: {books: bookList}})
} catch(err) {
console.log(err.message)
}
diff --git a/exercises/12_spa/books/modules/__mocks__/__mockData__/extractedData.json b/exercises/12_spa/books/modules/__mocks__/__mockData__/extractedData.json
new file mode 100644
index 0000000..238c353
--- /dev/null
+++ b/exercises/12_spa/books/modules/__mocks__/__mockData__/extractedData.json
@@ -0,0 +1,80 @@
+[
+ {
+ "title": "Thinking in Java",
+ "isbn": 9780131002876
+ },
+ {
+ "title": "Practical Java",
+ "isbn": 9780201616460
+ },
+ {
+ "title": "Java in a Time of Revolution",
+ "isbn": 9789793780146
+ },
+ {
+ "title": "Java",
+ "isbn": 9780764535437
+ },
+ {
+ "title": "The History of Java"
+ },
+ {
+ "title": "The Religion of Java",
+ "isbn": 9780226285108
+ },
+ {
+ "title": "Natural Language Processing with Java",
+ "isbn": 9781784398941
+ },
+ {
+ "title": "Java and Modern Europe",
+ "isbn": 9780700704330
+ },
+ {
+ "title": "Java Web Development Illuminated",
+ "isbn": 9780763734237
+ },
+ {
+ "title": "Introduction to Neural Networks with Java",
+ "isbn": 9781604390087
+ },
+ {
+ "title": "TCP/IP Sockets in Java",
+ "isbn": 9780080568782
+ },
+ {
+ "title": "Java in a Nutshell",
+ "isbn": 9780596007737
+ },
+ {
+ "title": "Java and XSLT",
+ "isbn": 9780596001438
+ },
+ {
+ "title": "Wicked Cool Java",
+ "isbn": 9781593270612
+ },
+ {
+ "title": "Java NIO Ron Hitchens",
+ "isbn": 9780596002886
+ },
+ {
+ "title": "Effective Java",
+ "isbn": 9780132778046
+ },
+ {
+ "title": "Java I/O",
+ "isbn": 9781449390884
+ },
+ {
+ "title": "The History of Java"
+ },
+ {
+ "title": "Programming with Java",
+ "isbn": 9780070141698
+ },
+ {
+ "title": "Concurrent Programming in Java",
+ "isbn": 9780201310092
+ }
+]
diff --git a/exercises/12_spa/books/modules/__mocks__/__mockData__/java.json b/exercises/12_spa/books/modules/__mocks__/__mockData__/java.json
index c1b56db..0e371c1 100644
--- a/exercises/12_spa/books/modules/__mocks__/__mockData__/java.json
+++ b/exercises/12_spa/books/modules/__mocks__/__mockData__/java.json
@@ -1,9 +1,25 @@
{
"items": [
+ {
+ "id": "Ql6QgWf6i7cC",
+ "volumeInfo": {
+ "title": "Thinking in Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0131002872"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780131002876"
+ }
+ ]
+ }
+ },
{
"id": "iWPeqljHNcoC",
"volumeInfo": {
- "title": "Java Book One",
+ "title": "Practical Java",
"industryIdentifiers": [
{
"type": "ISBN_10",
@@ -19,7 +35,7 @@
{
"id": "87totx4p3ZcC",
"volumeInfo": {
- "title": "Java Book Two",
+ "title": "Java in a Time of Revolution",
"industryIdentifiers": [
{
"type": "ISBN_13",
@@ -31,6 +47,270 @@
}
]
}
+ },
+ {
+ "id": "Ql0UWMUu3ooC",
+ "volumeInfo": {
+ "title": "Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0764535439"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780764535437"
+ }
+ ]
+ }
+ },
+ {
+ "id": "gJEC2q7DzpQC",
+ "volumeInfo": {
+ "title": "The History of Java",
+ "industryIdentifiers": [
+ {
+ "type": "OTHER",
+ "identifier": "HARVARD:32044021066998"
+ }
+ ]
+ }
+ },
+ {
+ "id": "-SYM4PW-YAgC",
+ "volumeInfo": {
+ "title": "The Religion of Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0226285103"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780226285108"
+ }
+ ]
+ }
+ },
+ {
+ "id": "q7y4BwAAQBAJ",
+ "volumeInfo": {
+ "title": "Natural Language Processing with Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_13",
+ "identifier": "9781784398941"
+ },
+ {
+ "type": "ISBN_10",
+ "identifier": "1784398942"
+ }
+ ]
+ }
+ },
+ {
+ "id": "qXayo7k3oakC",
+ "volumeInfo": {
+ "title": "Java and Modern Europe",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0700704337"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780700704330"
+ }
+ ]
+ }
+ },
+ {
+ "id": "oY9fShrQyUgC",
+ "volumeInfo": {
+ "title": "Java Web Development Illuminated",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0763734233"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780763734237"
+ }
+ ]
+ }
+ },
+ {
+ "id": "Swlcw7M4uD8C",
+ "volumeInfo": {
+ "title": "Introduction to Neural Networks with Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_13",
+ "identifier": "9781604390087"
+ },
+ {
+ "type": "ISBN_10",
+ "identifier": "1604390085"
+ }
+ ]
+ }
+ },
+ {
+ "id": "lfHo7uMk7r4C",
+ "volumeInfo": {
+ "title": "TCP/IP Sockets in Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0080568785"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780080568782"
+ }
+ ]
+ }
+ },
+ {
+ "id": "mvzgNSmHEUAC",
+ "volumeInfo": {
+ "title": "Java in a Nutshell",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0596007736"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780596007737"
+ }
+ ]
+ }
+ },
+ {
+ "id": "eSRnOKwU4hUC",
+ "volumeInfo": {
+ "title": "Java and XSLT",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0596001436"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780596001438"
+ }
+ ]
+ }
+ },
+ {
+ "id": "diqHjRjMhW0C",
+ "volumeInfo": {
+ "title": "Wicked Cool Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_13",
+ "identifier": "9781593270612"
+ },
+ {
+ "type": "ISBN_10",
+ "identifier": "1593270615"
+ }
+ ]
+ }
+ },
+ {
+ "id": "z7TQ8NSooS4C",
+ "volumeInfo": {
+ "title": "Java NIO Ron Hitchens",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0596002882"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780596002886"
+ }
+ ]
+ }
+ },
+ {
+ "id": "ka2VUBqHiWkC",
+ "volumeInfo": {
+ "title": "Effective Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0132778041"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780132778046"
+ }
+ ]
+ }
+ },
+ {
+ "id": "42etT_9-_9MC",
+ "volumeInfo": {
+ "title": "Java I/O",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "1449390889"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9781449390884"
+ }
+ ]
+ }
+ },
+ {
+ "id": "_-dCAAAAcAAJ",
+ "volumeInfo": {
+ "title": "The History of Java",
+ "industryIdentifiers": [
+ {
+ "type": "OTHER",
+ "identifier": "BSB:BSB10359645"
+ }
+ ]
+ }
+ },
+ {
+ "id": "ZdBYoyWIsMQC",
+ "volumeInfo": {
+ "title": "Programming with Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "007014169X"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780070141698"
+ }
+ ]
+ }
+ },
+ {
+ "id": "-x1S4neCSOYC",
+ "volumeInfo": {
+ "title": "Concurrent Programming in Java",
+ "industryIdentifiers": [
+ {
+ "type": "ISBN_10",
+ "identifier": "0201310090"
+ },
+ {
+ "type": "ISBN_13",
+ "identifier": "9780201310092"
+ }
+ ]
+ }
}
]
}
\ No newline at end of file
diff --git a/exercises/12_spa/books/modules/__mocks__/__mockData__/req.json b/exercises/12_spa/books/modules/__mocks__/__mockData__/req.json
new file mode 100644
index 0000000..34630eb
--- /dev/null
+++ b/exercises/12_spa/books/modules/__mocks__/__mockData__/req.json
@@ -0,0 +1,12 @@
+{
+ "query": {
+ "q": "java"
+ },
+ "params": {},
+ "method": "GET",
+ "url": "/?q=java",
+ "headers": {
+ "host": "xxx",
+ "user-agent": "xxx"
+ }
+}
\ No newline at end of file
diff --git a/exercises/12_spa/books/modules/__mocks__/__mockData__/validTable.txt b/exercises/12_spa/books/modules/__mocks__/__mockData__/validTable.txt
new file mode 100644
index 0000000..8624253
--- /dev/null
+++ b/exercises/12_spa/books/modules/__mocks__/__mockData__/validTable.txt
@@ -0,0 +1,82 @@
+
\ No newline at end of file
diff --git a/exercises/12_spa/books/modules/books.js b/exercises/12_spa/books/modules/books.js
index 544546d..0778713 100644
--- a/exercises/12_spa/books/modules/books.js
+++ b/exercises/12_spa/books/modules/books.js
@@ -7,7 +7,7 @@ const rest = require('rest')
const google = require('./google')
-const minQueryLen = 3
+//const minQueryLen = 3
// module.exports.search = request => new Promise( async resolve => {
// console.log(request.query)
@@ -20,6 +20,19 @@ const minQueryLen = 3
// //console.log(data)
// })
+module.exports.search = async request => {
+ if(request.query === undefined) {
+ return ''
+ }
+ if(request.query.q === undefined) {
+ return ''
+ }
+ const data = await this.searchGoogle(request.query.q)
+ const books = this.extractFields(data)
+ const table = this.buildTable(books)
+ return table
+}
+
/* -------------------------------------------------------------------------- */
/** Makes a Google Books API query
@@ -27,28 +40,49 @@ const minQueryLen = 3
* @param {String} searchString the URL to use for the query
*/
module.exports.searchGoogle = async searchString => {
- //console.log('calling function searchGoogle')
- //console.log(searchString)
const data = await google.search(searchString)
- //console.log(data)
return data
}
+/** Extracts data from the json
+ *
+ * @param {String} jsonStr the json string to parse
+ * @returns {Array} an array of book details
+ */
module.exports.extractFields = jsonStr => {
const bookArray = []
+ if(typeof jsonStr !== 'string') {
+ throw new Error('parameter has invalid data type')
+ }
const json = JSON.parse(jsonStr)
if(!Array.isArray(json.items)) throw new Error('no book data found')
for(const n of json.items) {
- let item = {}
- console.log(n)
+ const item = {}
item.title = n.volumeInfo.title
- for(const m in n.industryIdentifiers) {
- //console.log(m)
+ for(const m of n.volumeInfo.industryIdentifiers) {
if(m.type === 'ISBN_13') {
- // xxx
+ item.isbn = parseInt(m.identifier)
}
}
bookArray.push(item)
}
return bookArray
}
+
+module.exports.buildTable = bookArray => {
+ if(typeof bookArray !== 'object') {
+ throw new Error('invalid parameter data type')
+ }
+ if(!Array.isArray(bookArray)) {
+ throw new Error('invalid parameter data type')
+ }
+ let result = '\n'
+ for(const n of bookArray) {
+ result += `
+ ${n.title} |
+ ${n.isbn} |
+
`
+ }
+ result += '
'
+ return result
+}
\ No newline at end of file
diff --git a/exercises/12_spa/books/modules/google.js b/exercises/12_spa/books/modules/google.js
index 3e407c7..4fdfba7 100644
--- a/exercises/12_spa/books/modules/google.js
+++ b/exercises/12_spa/books/modules/google.js
@@ -11,9 +11,7 @@ const utility = require('./utility')
* @param {String} searchString the URL to use for the query
*/
module.exports.search = async searchString => {
- //console.log('REGULAR function searchGoogle')
- const url = utility.buildString(searchString, 2)
- //console.log(url)
+ const url = utility.buildString(searchString, 20)
const data = await rest(url)
//console.log(data.entity)
return data.entity
diff --git a/exercises/12_spa/books/public/style.css b/exercises/12_spa/books/public/style.css
new file mode 100644
index 0000000..e69de29