From dad019ce72e48a096901b0c0ba8a94bf076bc68c Mon Sep 17 00:00:00 2001 From: dodget Date: Thu, 26 Sep 2019 08:52:07 -0400 Subject: [PATCH 01/12] Refactoring functions. --- js/TimeExplorer.js | 74 ++++++++++------------------------------------ 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index 810a13c..6f22a20 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -4,11 +4,7 @@ * @param {array} values - Array of values for output array */ function zip_arrays(keys, values) { - var returnValues = {}; - values.forEach(function(val, i) { - returnValues[keys[i]] = val; - }); - return returnValues + return Object.assign(...keys.map((v,i) => ( {[v]: values[i]} ))); } /** @@ -32,62 +28,28 @@ function get_url_params() { * @param {string} timestring - A string representing a time, such as "10:30am" */ function timeParse(timestring) { - var ampm_match = timestring.match(/(.*)[AP]M$/); - if (ampm_match) { - var pm_match = timestring.match(/(.*)pm\s*$/i); - if (pm_match) { - var timeArray = pm_match[1].split(':'); - if (timeArray.length == 2) { - var hour = parseInt(timeArray[0])+12; - var minute = parseInt(timeArray[1]); - if (hour == 24) { - hour = 12; - } - return [hour,minute]; - } else { - return null; - } - } else { - var timeArray = ampm_match[1].split(':'); - if (timeArray.length == 2) { - var hour = parseInt(timeArray[0]); - var minute = parseInt(timeArray[1]); - if (hour == 12) { - hour = 24; - } - return [hour,minute]; - } else { - return null; - } + let pmMatch = timestring.match(/(.*)pm\s*$/i); + let timeArray = pmMatch ? pmMatch[1].split(':') : timestring.split(':'); + if (timeArray.length === 2) { + let hour = pmMatch ? parseInt(timeArray[0])+12 : parseInt(timeArray[0]); + let minute = parseInt(timeArray[1]); + if (pmMatch && hour == 12) { + hour = 24; } + return [hour,minute] } else { - var timeArray = timestring.split(':'); - if (timeArray.length == 2) { - var hour = parseInt(timeArray[0]); - var minute = parseInt(timeArray[1]); - return [hour,minute]; - } else { - return null; - } + return null; } } /** * Zero-pads a number to a 4-digit string */ -function padToNDigit(number,nDigits) { - var str = "" + number - if (str[0] == "-" ){ - str = str.slice(1); - var pad = Array(nDigits+1).join("0"); - var ans = pad.substring(0, pad.length - str.length) + str; - ans = "-" + ans; - return ans - } else { - var pad = Array(nDigits+1).join("0"); - var ans = pad.substring(0, pad.length - str.length) + str; - return ans - } +function padToNDigit(number, nDigits) { + let str = String(number); + let pad = Array(nDigits+1).join("0"); + let insert = str[0] === "-" ? 1 : 0; + return str.slice(0, insert) + pad + str.slice(insert); } /** @@ -95,11 +57,7 @@ function padToNDigit(number,nDigits) { * @param {string} id_string - string to be modified */ function plainId(id_string) { - if (id_string.startsWith('#')) { - return id_string.slice(1); - } else { - return id_string; - } + return id_string.startsWith('#') ? id_string.slice(1) : id_string; } /** From cdf1474c40c1eaea1a8a05da96df8e688ac1f13b Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 4 Oct 2019 13:30:55 -0400 Subject: [PATCH 02/12] Add basic unit testing with Jasmine. --- SpecRunner.html | 14 ++++++++++++++ js/TimeExplorerSpec.js | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 SpecRunner.html create mode 100644 js/TimeExplorerSpec.js diff --git a/SpecRunner.html b/SpecRunner.html new file mode 100644 index 0000000..eeb9098 --- /dev/null +++ b/SpecRunner.html @@ -0,0 +1,14 @@ + + + + Testing with Jasmine + + + + + + + + + + diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js new file mode 100644 index 0000000..805612e --- /dev/null +++ b/js/TimeExplorerSpec.js @@ -0,0 +1,41 @@ +describe('Testing the functions of the TimeExplorer file', ()=>{ + + it('zip_arrays should zip two arrays of equal length', ()=>{ + const arr1 = ["Joan", "Bill", "Bob"]; + const arr2 = [1,2,3]; + expect(zip_arrays(arr1,arr2)).toEqual({Joan: 1, Bill: 2, Bob: 3}); + }) + + it('timeParse should parse AM time', ()=>{ + const times = [ + "10:20", + "10:20am", + "10:20AM", + "10:20aM" + ]; + times.forEach( time => expect(timeParse(time)).toEqual([10,20])); + }) + + it('timeParse should parse PM time', ()=>{ + const times = [ + "10:20pm", + "10:20PM", + "10:20pM" + ]; + times.forEach( time => expect(timeParse(time)).toEqual([22,20])); + }) + + it('padToNDigit should pad a given number by given digits', ()=> { + const num = 6; + const negativeNum = -6; + const digits = 3; + expect(padToNDigit(num, digits)).toEqual("0006"); + expect(padToNDigit(negativeNum, digits)).toEqual("-0006"); + }) + + it('plainId should return id string without preceding #', ()=> { + const ids = ["m1id1sgreat", "#m1id1sgreat"]; + ids.forEach( id => expect(plainId(id)).toEqual("m1id1sgreat")); + }) + +}) From db072a808de9970165fa99740a8645d719877b3a Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 4 Oct 2019 16:01:01 -0400 Subject: [PATCH 03/12] Add test for GetDisplayDate. --- js/TimeExplorer.js | 48 ++++++++++++++++++------------------------ js/TimeExplorerSpec.js | 27 +++++++++++++++++++++++- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index 6f22a20..0073fee 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -68,33 +68,27 @@ function plainId(id_string) { * @param {datetime} startDate * @param {datetime} endDate */ -function GetDisplayDate(row,startDate,endDate) { - if (startDate == null) { - return null; - } - var months = ["January","February","March","April","May","June","July","August","September","October","November","December"]; - displayDate = ""; - if (row['Month'] && row['Month'] != "") { displayDate += months[startDate.getMonth()]+" "; } - if (row['Day'] && row['Day'] != "") { displayDate += startDate.getDate()+", "; } - if (row['Year'] && row['Year'] != "") { displayDate += startDate.getFullYear(); } - if (row['Time'] && row['Time'] != "") { - displayDate += " at " + startDate.getHours() + ":"; - var minutes = String(startDate.getMinutes()).length == 1 ? "0"+String(startDate.getMinutes()) : String(startDate.getMinutes()); - displayDate += minutes; - } - if (endDate) { - displayDate += " - "; - if (row['End Month'] && row['End Month'] != "") { displayDate += months[endDate.getMonth()]+" "; } - if (row['End Day'] && row['End Day'] != "") { displayDate += endDate.getDate()+", "; } - if (row['End Year'] && row['End Year'] != "") { displayDate += endDate.getFullYear(); } - if (row['End Time'] && row['End Time'] != "") { - displayDate += " at " + endDate.getHours() + ":"; - var minutes = String(endDate.getMinutes()).length == 1 ? "0"+String(endDate.getMinutes()) : String(endDate.getMinutes()); - displayDate += minutes; - } - } - return displayDate; -} + function GetDisplayDate(row, startDate, endDate) { + if (startDate == null) {return null;} + let displayDate = ""; + displayDate = appendDate(row, displayDate, startDate, ""); + if(endDate != null) {displayDate = appendDate(row, displayDate, endDate, "End ");} + return displayDate; + + function appendDate(row, displayDate, date, rowIndicator) { + const months = ["January","February","March","April","May","June","July","August","September","October","November","December"]; + if (rowIndicator === "End ") {displayDate += " - ";} + if (row[rowIndicator + 'Month'] && row[rowIndicator + 'Month'] != "") { displayDate += months[date.getMonth()]+" "; } + if (row[rowIndicator + 'Day'] && row[rowIndicator + 'Day'] != "") { displayDate += date.getDate()+", "; } + if (row[rowIndicator + 'Year'] && row[rowIndicator + 'Year'] != "") { displayDate += date.getFullYear(); } + if (row[rowIndicator + 'Time'] && row[rowIndicator + 'Time'] != "") { + displayDate += " at " + date.getHours() + ":"; + let minutes = String(date.getMinutes()).length == 1 ? "0"+String(date.getMinutes()) : String(date.getMinutes()); + displayDate += minutes; + } + return displayDate; + } + } /** * // TODO: What is this function for? diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index 805612e..e448c5e 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -3,7 +3,7 @@ describe('Testing the functions of the TimeExplorer file', ()=>{ it('zip_arrays should zip two arrays of equal length', ()=>{ const arr1 = ["Joan", "Bill", "Bob"]; const arr2 = [1,2,3]; - expect(zip_arrays(arr1,arr2)).toEqual({Joan: 1, Bill: 2, Bob: 3}); + expect(zip_arrays(arr1,arr2)).toEqual({"Joan": 1, "Bill": 2, "Bob": 3}); }) it('timeParse should parse AM time', ()=>{ @@ -38,4 +38,29 @@ describe('Testing the functions of the TimeExplorer file', ()=>{ ids.forEach( id => expect(plainId(id)).toEqual("m1id1sgreat")); }) + it('GetDisplayDate should return a formated timeframe', ()=> { + const row1 = { + 'Month': 11, + 'Day': 10, + 'Year': 1987, + 'Time': "10:30am", + 'End Month': 11, + 'End Day': 10, + 'End Year': 2001, + 'End Time': "10:30am" + } + const row2 = { + 'Month': 11, + 'Day': 10, + 'Year': 1987, + 'Time': "10:30am" + } + let end = new Date("June 6 2019 2:30"); + let start = new Date("September 9 1987 12:30"); + expect(GetDisplayDate(row1, start, end)).toEqual("September 9, 1987 at 12:30 - June 6, 2019 at 2:30"); + expect(GetDisplayDate(row2, start, end)).toEqual("September 9, 1987 at 12:30 - "); + expect(GetDisplayDate(row1, start)).toEqual("September 9, 1987 at 12:30"); + expect(GetDisplayDate(row2, start)).toEqual("September 9, 1987 at 12:30"); + }) + }) From 9a6c9e77c1a1c81a9008580d179111aa939d8ace Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 18 Oct 2019 13:11:20 -0400 Subject: [PATCH 04/12] Testing TimeExplorer class. --- SpecRunner.html | 10 ++++++++++ js/TimeExplorerSpec.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/SpecRunner.html b/SpecRunner.html index eeb9098..913c327 100644 --- a/SpecRunner.html +++ b/SpecRunner.html @@ -2,10 +2,20 @@ Testing with Jasmine + + + + + + + + + + diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index e448c5e..fc7b6c4 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -64,3 +64,39 @@ describe('Testing the functions of the TimeExplorer file', ()=>{ }) }) + +describe('Testing the TimeExplorer class', () => { + let div; + + beforeEach( ()=> { + div = document.createElement('div'); + div.setAttribute("id", "timeline"); + document.body.appendChild(div); + }); + + afterEach( ()=> { + div.remove(); + div = null; + }); + + it('TimeExplorer should have options after initialization', ()=> { + const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" + let explorer = new TimeExplorer(api_key); + expect(explorer.options.timelineOptions.height).toEqual(window.innerHeight); + }) + + it('TimeExplorer.get_tag_col() should return "Tags"', ()=> { + const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" + let explorer = new TimeExplorer(api_key); + expect(explorer.get_tag_col()).toEqual('Tags'); + }) + + it('TimeExplorer.set_options() should extend options', ()=> { + const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" + let explorer = new TimeExplorer(api_key, options=["Joe"]); + let r = explorer.set_options(["Joe"]) + expect(r.timelineOptions['0']).toEqual("Joe"); + }) + + +}) From 975dff930fcb48221fb8ad5942b85b4bf0096100 Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 18 Oct 2019 14:15:21 -0400 Subject: [PATCH 05/12] Refactor tests. --- js/TimeExplorerSpec.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index fc7b6c4..d57caa0 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -1,12 +1,12 @@ -describe('Testing the functions of the TimeExplorer file', ()=>{ +describe('Testing the functions of the TimeExplorer file', ()=> { - it('zip_arrays should zip two arrays of equal length', ()=>{ + it('zip_arrays should zip two arrays of equal length', ()=> { const arr1 = ["Joan", "Bill", "Bob"]; const arr2 = [1,2,3]; expect(zip_arrays(arr1,arr2)).toEqual({"Joan": 1, "Bill": 2, "Bob": 3}); }) - it('timeParse should parse AM time', ()=>{ + it('timeParse should parse AM time', ()=> { const times = [ "10:20", "10:20am", @@ -16,7 +16,7 @@ describe('Testing the functions of the TimeExplorer file', ()=>{ times.forEach( time => expect(timeParse(time)).toEqual([10,20])); }) - it('timeParse should parse PM time', ()=>{ + it('timeParse should parse PM time', ()=> { const times = [ "10:20pm", "10:20PM", @@ -67,6 +67,10 @@ describe('Testing the functions of the TimeExplorer file', ()=>{ describe('Testing the TimeExplorer class', () => { let div; + const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE"; + const new_explorer = () => { + return new TimeExplorer(api_key); + } beforeEach( ()=> { div = document.createElement('div'); @@ -80,23 +84,27 @@ describe('Testing the TimeExplorer class', () => { }); it('TimeExplorer should have options after initialization', ()=> { - const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" - let explorer = new TimeExplorer(api_key); + explorer = new_explorer() expect(explorer.options.timelineOptions.height).toEqual(window.innerHeight); }) it('TimeExplorer.get_tag_col() should return "Tags"', ()=> { - const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" - let explorer = new TimeExplorer(api_key); - expect(explorer.get_tag_col()).toEqual('Tags'); + explorer = new_explorer() + const tags = explorer.get_tag_col(); + expect(tags).toEqual('Tags'); }) it('TimeExplorer.set_options() should extend options', ()=> { - const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE" - let explorer = new TimeExplorer(api_key, options=["Joe"]); - let r = explorer.set_options(["Joe"]) + explorer = new_explorer() + const r = explorer.set_options(["Joe"]) expect(r.timelineOptions['0']).toEqual("Joe"); }) + it('TimeExplorer.slugify() should return a valid slug', ()=> { + explorer = new_explorer() + const slug = explorer.slugify("Let's make a slug"); + expect(slug).toEqual("Let_s_make_a_slug"); + }) + }) From 68c6e8d7187247b254f2c787bfa0e4b871a580ec Mon Sep 17 00:00:00 2001 From: dodget Date: Wed, 23 Oct 2019 16:17:36 -0400 Subject: [PATCH 06/12] Add a few more tests for methods. --- SpecRunner.html | 2 ++ js/TimeExplorer.js | 82 +++++++++++++++--------------------------- js/TimeExplorerSpec.js | 27 ++++++++++++++ 3 files changed, 58 insertions(+), 53 deletions(-) diff --git a/SpecRunner.html b/SpecRunner.html index 913c327..eecbd0b 100644 --- a/SpecRunner.html +++ b/SpecRunner.html @@ -7,6 +7,8 @@ + + diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index 0073fee..b0947fb 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -113,9 +113,7 @@ function testFilter(self) { * @param {string} displayDate - String representing date as it should be displayed */ function GetDisplayTitle(row,displayDate) { - var output = "

"+row['Headline']+"

"; - output += "

"+displayDate+"

" - return output; + return "

" + row['Headline'] + "

"+displayDate+"

"; } /** @@ -273,12 +271,7 @@ class TimeExplorer { } get_tag_col() { - var url_params = get_url_params(); - if ('tag_col' in url_params) { - return url_params['tag_col']; - } else { - return 'Tags'; - } + return 'tag_col' in get_url_params() ? url_params['tag_col'] : 'Tags'; } /** @@ -344,9 +337,8 @@ class TimeExplorer { * */ create_timeline(options) { - var container = document.getElementById('ft-visualization'); - var timeline = new vis.Timeline(container,options.timelineOptions); - return timeline; + let container = document.getElementById('ft-visualization'); + return new vis.Timeline(container,options.timelineOptions); } /** @@ -559,12 +551,10 @@ class TimeExplorer { * @uses get_sheet_data */ get_all_sheet_data() { - var self = this; - var promises = []; - for (var i = 0; i < this.sheet_ids.length; i++) { - var sheet_id = this.sheet_ids[i]; - promises.push(this.get_sheet_data(sheet_id)); - }; + let self = this; + const promises = this.sheet_id.map( (id) => { + return this.get_sheet_data(id); + }); return $.when.apply($,promises); }; @@ -764,28 +754,24 @@ class TimeExplorer { * Uses groups from Timeline JS to color the timeline. */ set_groups(self) { - var groups = []; - for (var i = 0; i < self.items.length; i++) { - if (self.items[i]['sheet_group']) { - var group = self.items[i]['sheet_group']; - var slug = self.slugify(group); - if ($.inArray(group,groups) == -1) { - groups.push(group); - } - self.items[i]['className'] = slug; + let groups = self.items.map( (item)=> { + if (item['sheet_group']) { + item['className'] = self.slugify(item['sheet_group']); + return item['sheet_group']; } else { - self.items[i]['className'] = "Ungrouped"; - self.items[i]['group_slug'] = "Ungrouped"; - if ($.inArray('Ungrouped',groups) == -1) { - groups.push("Ungrouped"); - } + item['className'] = "Ungrouped"; + item['group_slug'] = "Ungrouped"; + return "Ungrouped"; } - } + }); + groups.filter(self.onlyUnique); groups.sort(); self.setup_group_ui(self, groups); return groups; } + + /** * Sets up color scheme and filters for groups. */ @@ -834,19 +820,15 @@ class TimeExplorer { * Sets up tags to be used as filters */ set_tags(self) { - var tags = []; - for (var i = 0; i < self.items.length; i++) { - if (self.items[i]['tags']) { - var these_tags = self.items[i]['tags']; - var slugs = these_tags.map(self.slugify); - tags = tags.concat(these_tags); - if (self.items[i]['className']) { - self.items[i]['className'] = self.items[i]['className'] + ' ' + slugs.join(' '); - } else { - self.items[i]['className'] = slugs.join(' '); - } + let tags = []; + self.items.forEach( (item)=> { + if (item['tags']) { + let slugs = item['tags'].map(self.slugify); + let concatter = item['classname'] ? item['classname'] : ''; + item['classname'] = concatter + ' ' + slugs.join(' '); + tags = tags.concat(item['tags']); } - } + }); tags = tags.filter( self.onlyUnique ); tags.sort(); self.setup_filters(self,tags,"Tags"); @@ -1058,14 +1040,8 @@ class TimeExplorer { * @param {string} text - the text to be made into a slug. */ slugify(text) { - if (typeof(text) == "string") { - var output = text.trim() - var pattern = /[\s~!@$%^&*()+=,./';:"?><[\] \\{}|`#]+/g - output = output.replace(pattern,'_') - return output - } else { - return ""; - } + const pattern = /[\s~!@$%^&*()+=,./';:"?><[\] \\{}|`#]+/g; + return typeof(text) == "string" ? text.trim().replace(pattern,'_') : ""; } /** diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index d57caa0..b34021c 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -38,6 +38,14 @@ describe('Testing the functions of the TimeExplorer file', ()=> { ids.forEach( id => expect(plainId(id)).toEqual("m1id1sgreat")); }) + it('GetDisplayTitle should return html with title', ()=> { + const row1 = { + 'Headline': "HEADLINE", + } + const displayDate = "July 6th"; + expect(GetDisplayTitle(row1, displayDate)).toBe('

HEADLINE

July 6th

'); + }) + it('GetDisplayDate should return a formated timeframe', ()=> { const row1 = { 'Month': 11, @@ -106,5 +114,24 @@ describe('Testing the TimeExplorer class', () => { expect(slug).toEqual("Let_s_make_a_slug"); }) + it('TimeExplorer.set_tags() return all tags', ()=> { + explorer = new_explorer() + explorer.items = [{'tags': ["Joe"]}, {'tags': ["Mary", "Liam"]}]; + const tag_return = explorer.set_tags(explorer) + expect(tag_return).toEqual([ 'Joe', 'Liam', 'Mary' ]); + }) + + it('TimeExplorer.set_groups() return all groups', ()=> { + explorer = new_explorer() + explorer.items = [{'sheet_group': 1}, {'sheet_group': 2}]; + const group_return = explorer.set_groups(explorer) + expect(group_return).toEqual([1,2]); + }) + + + + + + }) From 1ad36738a237bafe2dddc0f0814797c442bbf5ad Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 25 Oct 2019 08:33:00 -0400 Subject: [PATCH 07/12] Add more testing and refactoring. --- js/TimeExplorer.js | 25 +++++++------------ js/TimeExplorerSpec.js | 56 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index b0947fb..e9478ba 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -915,23 +915,16 @@ class TimeExplorer { */ set_filters(slug, self) { // Set Group filters - var activeGroups = []; - var groupCheckboxes = $(".Groups input.filter-checkbox"); - for (var i = 0; i < groupCheckboxes.length; i++) { - if (groupCheckboxes[i].checked) { - activeGroups.push(groupCheckboxes[i].value); - } - } - self.filters.activeGroups = activeGroups; + const groupCheckboxes = $(".Groups input.filter-checkbox"); + self.filters.activeGroups = Array.prototype.map.call(groupCheckboxes, (box)=> { + if (box.checked) {return box.value;} + }); // Set Tag filters - var activeTags = []; - var tagCheckboxes = $(".Tags input.filter-checkbox"); - for (var i = 0; i < tagCheckboxes.length; i++) { - if (tagCheckboxes[i].checked) { - activeTags.push(tagCheckboxes[i].value); - } - } - self.filters.activeTags = activeTags; + const tagCheckboxes = $(".Tags input.filter-checkbox"); + self.filters.activeTags = Array.prototype.map.call(tagCheckboxes, (box)=> { + if(box.checked) {return box.value;} + }); + if ($("#tag-options").length > 0) { if ($("#tag-option-any")[0].checked) { self.filters.tagOptions = "any"; diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index b34021c..66a25ab 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -74,6 +74,7 @@ describe('Testing the functions of the TimeExplorer file', ()=> { }) describe('Testing the TimeExplorer class', () => { + let el; let div; const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE"; const new_explorer = () => { @@ -81,14 +82,18 @@ describe('Testing the TimeExplorer class', () => { } beforeEach( ()=> { + el = document.createElement('html'); div = document.createElement('div'); div.setAttribute("id", "timeline"); - document.body.appendChild(div); + document.body.appendChild(el); + el.appendChild(div); }); afterEach( ()=> { div.remove(); div = null; + el.remove(); + el = null; }); it('TimeExplorer should have options after initialization', ()=> { @@ -128,10 +133,55 @@ describe('Testing the TimeExplorer class', () => { expect(group_return).toEqual([1,2]); }) + it('TimeExplorer.set_filters() set filters some things checked', ()=> { + // groups + group = addTestElement('div', 'Groups'); + inp1 = addTestElement('input', 'filter-checkbox', 'Event', true); + inp2 = addTestElement('input', 'filter-checkbox', 'Thing', true); + group.appendChild(inp1); + group.appendChild(inp2); + + //tags + tag = addTestElement('div', 'Tags'); + inp3 = addTestElement('input', 'filter-checkbox', 'TAG', true); + inp4 = addTestElement('input', 'filter-checkbox', 'Another', true); + tag.appendChild(inp3); + tag.appendChild(inp4); + + // attach them to the html + el.appendChild(group); + el.appendChild(tag); + explorer = new_explorer() + explorer.set_filters('none', explorer) + expect(explorer.filters.tagOptions).toBe('any'); + expect(explorer.filters.activeGroups).toEqual([ 'Event', 'Thing' ]); + expect(explorer.filters.activeTags).toEqual([ 'TAG', 'Another' ]); + + // remove it all + inp1.remove(); + inp2.remove(); + inp3.remove(); + inp4.remove(); + group.remove(); + tag.remove(); + }) +}) - -}) +// Helper function to set up an element to add for testing +const addTestElement = (elem, cls, val, checked) => { + item = document.createElement(elem); + if(cls) { + item.setAttribute("class", cls); + } + if(val) { + item.setAttribute("value", val); + } + if(checked) { + item.setAttribute("checked", true); + } + return item; +} From 08744574690bb4459087389021c2a69d2510fddf Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 25 Oct 2019 08:42:01 -0400 Subject: [PATCH 08/12] Fix typo. --- js/TimeExplorer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index e9478ba..c01f558 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -552,7 +552,7 @@ class TimeExplorer { */ get_all_sheet_data() { let self = this; - const promises = this.sheet_id.map( (id) => { + const promises = this.sheet_ids.map( (id) => { return this.get_sheet_data(id); }); return $.when.apply($,promises); From 6cda8a95d57b815be866bab6bc2dbe4aeadbf6bc Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 25 Oct 2019 13:47:36 -0400 Subject: [PATCH 09/12] Add another unit test. --- js/TimeExplorerSpec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index 66a25ab..1a354f5 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -107,6 +107,12 @@ describe('Testing the TimeExplorer class', () => { expect(tags).toEqual('Tags'); }) + it('TimeExplorer.create_timeline() should create a timeline', ()=> { + explorer = new_explorer() + const timeline = explorer.create_timeline(explorer.options); + expect(Object.keys(timeline)).toContain('itemsData'); + }) + it('TimeExplorer.set_options() should extend options', ()=> { explorer = new_explorer() const r = explorer.set_options(["Joe"]) From 7dadafc08fd7adc77220f88989b8f2fbaef89b74 Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 25 Oct 2019 15:20:13 -0400 Subject: [PATCH 10/12] Add a few more method tests and refactoring. --- js/TimeExplorer.js | 15 +++++++-------- js/TimeExplorerSpec.js | 6 ++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index c01f558..c1da414 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -515,15 +515,14 @@ class TimeExplorer { * @param {string} sheet_id - ID of Google spreadsheet containing data */ get_sheet_data(sheet_id) { - var self = this; - var dfd = $.Deferred(); - var request_url = `https://sheets.googleapis.com/v4/spreadsheets/${sheet_id}/values/A:ZZZ?key=${this.api_key}`; + let self = this; + let dfd = $.Deferred(); + const request_url = `https://sheets.googleapis.com/v4/spreadsheets/${sheet_id}/values/A:ZZZ?key=${this.api_key}`; $.getJSON(request_url).done(function(data) { - var columns = data.values[0]; - for (var i = 1; i < data.values.length; i++) { - var values = zip_arrays(columns, data.values[i]); - self.sheet_data.push(values); - }; + let columns = data.values[0]; + self.sheet_data = data.values.slice(1).map( (item)=> { + return zip_arrays(columns,item); + }); dfd.resolve(); }); return dfd.promise(); diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index 1a354f5..4cc1ea9 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -113,6 +113,12 @@ describe('Testing the TimeExplorer class', () => { expect(Object.keys(timeline)).toContain('itemsData'); }) + it('TimeExplorer.get_sheet_data() should return a promise', ()=> { + explorer = new_explorer() + const sheetData = explorer.get_sheet_data(api_key); + expect(Object.keys(sheetData)).toContain("promise"); + }) + it('TimeExplorer.set_options() should extend options', ()=> { explorer = new_explorer() const r = explorer.set_options(["Joe"]) From 33e8bfb5b6c3c07a88da3820327c6ccddb76e712 Mon Sep 17 00:00:00 2001 From: dodget Date: Thu, 31 Oct 2019 10:03:10 -0400 Subject: [PATCH 11/12] Refactor tests. --- js/TimeExplorerSpec.js | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index 4cc1ea9..8de1837 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -76,6 +76,7 @@ describe('Testing the functions of the TimeExplorer file', ()=> { describe('Testing the TimeExplorer class', () => { let el; let div; + let explorer; const api_key = "AIzaSyCA8GIsjw-QL-CC1v6fgDWmDyyhRM_ZESE"; const new_explorer = () => { return new TimeExplorer(api_key); @@ -87,6 +88,7 @@ describe('Testing the TimeExplorer class', () => { div.setAttribute("id", "timeline"); document.body.appendChild(el); el.appendChild(div); + explorer = new_explorer() }); afterEach( ()=> { @@ -97,49 +99,41 @@ describe('Testing the TimeExplorer class', () => { }); it('TimeExplorer should have options after initialization', ()=> { - explorer = new_explorer() expect(explorer.options.timelineOptions.height).toEqual(window.innerHeight); }) it('TimeExplorer.get_tag_col() should return "Tags"', ()=> { - explorer = new_explorer() const tags = explorer.get_tag_col(); expect(tags).toEqual('Tags'); }) it('TimeExplorer.create_timeline() should create a timeline', ()=> { - explorer = new_explorer() const timeline = explorer.create_timeline(explorer.options); expect(Object.keys(timeline)).toContain('itemsData'); }) it('TimeExplorer.get_sheet_data() should return a promise', ()=> { - explorer = new_explorer() const sheetData = explorer.get_sheet_data(api_key); expect(Object.keys(sheetData)).toContain("promise"); }) it('TimeExplorer.set_options() should extend options', ()=> { - explorer = new_explorer() const r = explorer.set_options(["Joe"]) expect(r.timelineOptions['0']).toEqual("Joe"); }) it('TimeExplorer.slugify() should return a valid slug', ()=> { - explorer = new_explorer() const slug = explorer.slugify("Let's make a slug"); expect(slug).toEqual("Let_s_make_a_slug"); }) it('TimeExplorer.set_tags() return all tags', ()=> { - explorer = new_explorer() explorer.items = [{'tags': ["Joe"]}, {'tags': ["Mary", "Liam"]}]; const tag_return = explorer.set_tags(explorer) expect(tag_return).toEqual([ 'Joe', 'Liam', 'Mary' ]); }) it('TimeExplorer.set_groups() return all groups', ()=> { - explorer = new_explorer() explorer.items = [{'sheet_group': 1}, {'sheet_group': 2}]; const group_return = explorer.set_groups(explorer) expect(group_return).toEqual([1,2]); @@ -147,16 +141,16 @@ describe('Testing the TimeExplorer class', () => { it('TimeExplorer.set_filters() set filters some things checked', ()=> { // groups - group = addTestElement('div', 'Groups'); - inp1 = addTestElement('input', 'filter-checkbox', 'Event', true); - inp2 = addTestElement('input', 'filter-checkbox', 'Thing', true); + group = addTestElement('div', {'class':'Groups'}); + inp1 = addTestElement('input', {'class':'filter-checkbox', 'value':'Event', 'checked':true}); + inp2 = addTestElement('input', {'class':'filter-checkbox', 'value':'Thing', 'checked':true}); group.appendChild(inp1); group.appendChild(inp2); //tags - tag = addTestElement('div', 'Tags'); - inp3 = addTestElement('input', 'filter-checkbox', 'TAG', true); - inp4 = addTestElement('input', 'filter-checkbox', 'Another', true); + tag = addTestElement('div', {'class':'Tags'}); + inp3 = addTestElement('input', {'class':'filter-checkbox', 'value':'TAG', 'checked':true}); + inp4 = addTestElement('input', {'class':'filter-checkbox', 'value':'Another', 'checked':true}); tag.appendChild(inp3); tag.appendChild(inp4); @@ -164,7 +158,6 @@ describe('Testing the TimeExplorer class', () => { el.appendChild(group); el.appendChild(tag); - explorer = new_explorer() explorer.set_filters('none', explorer) expect(explorer.filters.tagOptions).toBe('any'); expect(explorer.filters.activeGroups).toEqual([ 'Event', 'Thing' ]); @@ -184,16 +177,10 @@ describe('Testing the TimeExplorer class', () => { // Helper function to set up an element to add for testing -const addTestElement = (elem, cls, val, checked) => { +const addTestElement = (elem, attrs) => { item = document.createElement(elem); - if(cls) { - item.setAttribute("class", cls); - } - if(val) { - item.setAttribute("value", val); - } - if(checked) { - item.setAttribute("checked", true); - } + Object.keys(attrs).forEach( (attribute) => { + item.setAttribute(attribute, attrs[attribute]); + }); return item; } From 2457c45385d1a1decd8fd1ecab0139f332d1fd20 Mon Sep 17 00:00:00 2001 From: dodget Date: Fri, 1 Nov 2019 15:23:11 -0400 Subject: [PATCH 12/12] Fix bug in padToNDigit. --- js/TimeExplorer.js | 4 +++- js/TimeExplorerSpec.js | 25 ++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/js/TimeExplorer.js b/js/TimeExplorer.js index c1da414..43cb296 100644 --- a/js/TimeExplorer.js +++ b/js/TimeExplorer.js @@ -47,7 +47,9 @@ function timeParse(timestring) { */ function padToNDigit(number, nDigits) { let str = String(number); - let pad = Array(nDigits+1).join("0"); + let currentLength = str[0] === '-' ? str.length - 1 : str.length; + let digits = nDigits - (currentLength - 1); + let pad = Array(digits).join("0"); let insert = str[0] === "-" ? 1 : 0; return str.slice(0, insert) + pad + str.slice(insert); } diff --git a/js/TimeExplorerSpec.js b/js/TimeExplorerSpec.js index 8de1837..9447a28 100644 --- a/js/TimeExplorerSpec.js +++ b/js/TimeExplorerSpec.js @@ -26,11 +26,11 @@ describe('Testing the functions of the TimeExplorer file', ()=> { }) it('padToNDigit should pad a given number by given digits', ()=> { - const num = 6; - const negativeNum = -6; - const digits = 3; - expect(padToNDigit(num, digits)).toEqual("0006"); - expect(padToNDigit(negativeNum, digits)).toEqual("-0006"); + const digits = 4; + expect(padToNDigit(6, digits)).toEqual("0006"); + expect(padToNDigit(-6, digits)).toEqual("-0006"); + expect(padToNDigit(1987, digits)).toEqual("1987"); + expect(padToNDigit(-1987, digits)).toEqual("-1987"); }) it('plainId should return id string without preceding #', ()=> { @@ -172,6 +172,21 @@ describe('Testing the TimeExplorer class', () => { tag.remove(); }) + it('TimeExplorer.constructDate() constructs a date.', () =>{ + let constructedDate = explorer.constructDate(); + expect(constructedDate).toEqual(null); + constructedDate = explorer.constructDate(year=6); + let date = new Date("0006-01-01T00:00"); + expect(constructedDate).toEqual(date); + constructedDate = explorer.constructDate(year=-6); + date = new Date("0000-01-01T00:00"); + date.setFullYear(-6); + expect(constructedDate).toEqual(date); + constructedDate = explorer.constructDate(year=1987); + date = new Date("1987-01-01T00:00"); + expect(constructedDate).toEqual(date); + }) + })