diff --git a/Gemfile b/Gemfile index c5d5d1fe1..1e26e67fc 100644 --- a/Gemfile +++ b/Gemfile @@ -78,8 +78,7 @@ gem 'omniauth-facebook' gem 'omniauth-flickr', '>= 0.0.15' gem 'omniauth-twitter' -# For charting data -gem 'd3-rails', '~> 3.5' # 4.* produces Error: : could not find an object to spy upon for linear() - see https://travis-ci.org/Growstuff/growstuff/jobs/204461482 +gem "chartkick" # client for Elasticsearch. Elasticsearch is a flexible # and powerful, distributed, real-time search and analytics engine. diff --git a/Gemfile.lock b/Gemfile.lock index 31113ea38..637ee3f44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,6 +99,7 @@ GEM capybara-screenshot (1.0.18) capybara (>= 1.0, < 3) launchy + chartkick (2.2.5) childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) climate_control (0.2.0) @@ -143,8 +144,6 @@ GEM crass (1.0.3) csv_shaper (1.3.0) activesupport (>= 3.0.0) - d3-rails (3.5.17) - railties (>= 3.1) dalli (2.7.6) database_cleaner (1.6.2) debug_inspector (0.0.3) @@ -569,12 +568,12 @@ DEPENDENCIES capybara capybara-email capybara-screenshot + chartkick codeclimate-test-reporter coffee-rails comfortable_mexican_sofa coveralls csv_shaper - d3-rails (~> 3.5) dalli database_cleaner devise diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index fc3369264..e0d7b8463 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,6 +10,8 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // +// = require Chart.bundle +// = require chartkick // = require leaflet // = require leaflet.markercluster // = require js-routes diff --git a/app/assets/javascripts/crops.js.erb b/app/assets/javascripts/crops.js.erb index afa16a009..4e2a67e82 100644 --- a/app/assets/javascripts/crops.js.erb +++ b/app/assets/javascripts/crops.js.erb @@ -1,16 +1,3 @@ -//= require graphs/horizontal_bar_graph - -if (document.getElementById("cropmap") !== null) { - mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; - mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; - mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; - - L.Icon.Default.imagePath = '/assets' - - cropmap = L.map('cropmap').setView([0.0, -0.0], 2); - showCropMap(cropmap); -} - function showCropMap(cropmap) { L.tileLayer(mapbox_base_url, { @@ -51,41 +38,20 @@ function showCropMap(cropmap) { cropmap.addLayer(markers); } -function plantingStats(crop) { - var sunniness_counts = { 'empty': 0, 'sun': 0, 'semi-shade': 0, 'shade': 0 }; - $.each(crop.plantings, function(i, planting) { - if (planting.sunniness) { - sunniness_counts[planting.sunniness]++; - } else { - sunniness_counts['Empty']++; - } +$(document).ready(function() { + + if (document.getElementById("cropmap") !== null) { + mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>"; + mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>"; + mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token; + + L.Icon.Default.imagePath = '/assets' + + cropmap = L.map('cropmap').setView([0.0, -0.0], 2); + showCropMap(cropmap); + } + + $('.btn.toggle.crop-hierarchy').click(function () { + $('.toggle.crop-hierarchy').toggleClass('hide'); }); - return [ - {name: 'Empty', value: sunniness_counts['empty']}, - {name: 'Sun', value: sunniness_counts['sun']}, - {name: 'Semi-shade', value: sunniness_counts['semi-shade']}, - {name: 'Shade', value: sunniness_counts['shade']} - ]; -} - -if ($("#sunchart")[0] !== null) { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - $.getJSON(location.pathname + '.json', function (crop) { - data = { - bars: plantingStats(crop), - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 100, scale: 'ordinal'}, - //left is used to shift the bars over so that there is - //room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100} - }; - - var graph = new HorizontalBarGraph(data); - graph.render(d3.select($('#sunchart')[0])); - }); -} - -$('.btn.toggle.crop-hierarchy').click(function () { - $('.toggle.crop-hierarchy').toggleClass('hide'); -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/graphs/bar_group.js b/app/assets/javascripts/graphs/bar_group.js deleted file mode 100644 index e8a90c20a..000000000 --- a/app/assets/javascripts/graphs/bar_group.js +++ /dev/null @@ -1,53 +0,0 @@ -// =require d3 -// = require graphs/width_scale -// = require graphs/height_scale - -/* - * This represents bars for a bar graph. - * Currently these are used for HorizontalBarGraph. - */ -(function() { - 'use strict'; - - - var growstuff = (window.growstuff = window.growstuff || {}); - var WidthScale = growstuff.WidthScale; - var HeightScale = growstuff.HeightScale; - -/** - * data object for bar group - * @param {Object} data Graph configuration - */ -function BarGroup(data) { - this._data = data; -} - BarGroup.prototype.render = function(root) { - var data = this._data; - var bars = this._data.bars; - var widthScale = new WidthScale(data).render(); - var heightScale = new HeightScale(data).render(); - - return root.append('g') - .attr('class', 'bar') - .selectAll('rect') - .data(bars.map(function(bar) { - return bar.value; - })) - .enter() - .append('rect') - .attr('y', function(d, i) { - return heightScale(i); - }) - .attr('height', heightScale.rangeBand()) - .attr('fill', data.bar_color) - .attr('width', function(d) { - return widthScale(d); - }) - .append('title') - .text(function(d) { - return 'This value is ' + d + '.'; - }); - }; - - growstuff.BarGroup = BarGroup; -}()); diff --git a/app/assets/javascripts/graphs/bar_label_group.js b/app/assets/javascripts/graphs/bar_label_group.js deleted file mode 100644 index 2da31f567..000000000 --- a/app/assets/javascripts/graphs/bar_label_group.js +++ /dev/null @@ -1,45 +0,0 @@ -// =require d3 -/** - * This file draws the labels to the left of each bar. - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - /** - * new bar label object - * @param {Object} data Graph configuration - */ - function BarLabelGroup(data) { - this._data = data; - } - - BarLabelGroup.prototype.render = function(d3) { - var bars = this._data.bars; - // vvcopy pasta from spike vv -- this is a good candidate for refactor - var barHeight = 40; - - return d3.append('g') - .attr('class', 'bar-label') - .selectAll('text') - .data(bars.map(function(bar) { - return bar.name; - })) - .enter() - .append('text') - .attr('x', -80) - .attr('y', function(d, i) { - // shrink the margin between each label to give them an even spread with - // bars - var barLabelSpread = 2/3; - // move them downward to line up with bars - var barLabelTopEdge = 17; - return i * barHeight * (barLabelSpread) + barLabelTopEdge; - }) - .text(function(d) { - return d; - }); - }; - - growstuff.BarLabelGroup = BarLabelGroup; -}()); diff --git a/app/assets/javascripts/graphs/height_scale.js b/app/assets/javascripts/graphs/height_scale.js deleted file mode 100644 index 5189ef800..000000000 --- a/app/assets/javascripts/graphs/height_scale.js +++ /dev/null @@ -1,31 +0,0 @@ -// =require d3 - -/** - * Height Scale is used to map the number of bars to the display size of - * the svg - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * new height scale object - * @param {Object} data Graph configuration - */ - function HeightScale(data) { - this._data = data; - } - - HeightScale.prototype.render = function() { - var data = this._data; - var scaleType = data.height.scale; - - return d3.scale[scaleType]() - .domain(d3.range(data.bars.length)) - .rangeRoundBands([0, data.height.size], 0.05, 0); - }; - - growstuff.HeightScale = HeightScale; -}()); diff --git a/app/assets/javascripts/graphs/horizontal_bar_graph.js b/app/assets/javascripts/graphs/horizontal_bar_graph.js deleted file mode 100644 index ef2333ef1..000000000 --- a/app/assets/javascripts/graphs/horizontal_bar_graph.js +++ /dev/null @@ -1,54 +0,0 @@ -// = require d3 -// = require graphs/bar_group -// = require graphs/bar_label_group - -/** - * Horizontal Bar Graph represents sum total of the graph including all of the parts: - * Bars - * Bar Labels - * - * The main dimensions of the graph are rendered here. - */ - -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - var BarGroup = growstuff.BarGroup; - var BarLabelGroup = growstuff.BarLabelGroup; - - /** - * create a new graph object - * @param {Object} data Graph configuration - */ - function HorizontalBarGraph(data) { - this._data = data; - this._d3 = d3; - } - - HorizontalBarGraph.prototype.render = function(root) { - var width = this._data.width; - var height = this._data.height; - - var barLabelGroup = new BarLabelGroup(this._data); - var margin = this._data.margin; - - var barGroup = new BarGroup(this._data); - - var svg = root - .append('svg') - .attr('width', width.size + margin.left + margin.right) - .attr('height', height.size + margin.top + margin.bottom) - .append('g') - .attr('class', 'bar-graph') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - - barGroup.render(svg); - barLabelGroup.render(svg); - - return svg; - }; - - growstuff.HorizontalBarGraph = HorizontalBarGraph; -}()); diff --git a/app/assets/javascripts/graphs/width_scale.js b/app/assets/javascripts/graphs/width_scale.js deleted file mode 100644 index 7f2b9dda2..000000000 --- a/app/assets/javascripts/graphs/width_scale.js +++ /dev/null @@ -1,37 +0,0 @@ -// =require d3 - -/** - * Width scale is used to map the value for the length of each bar - * to the display size of the svg - */ -(function() { - 'use strict'; - - var growstuff = (window.growstuff = window.growstuff || {}); - - /** - * Object for WidthScale - * @param {Object} data Graph configuration - */ - function WidthScale(data) { - this._data = data; - } - - WidthScale.prototype.render = function() { - var data = this._data; - var scaleType = data.width.scale; - var axisSize = data.width.size; - - return d3.scale[scaleType]() - .domain([0, this.getMaxValue()]) - .range([0, axisSize]); - }; - - WidthScale.prototype.getMaxValue = function() { - return d3.max(this._data.bars.map(function(bar) { - return bar.value; - })); - }; - - growstuff.WidthScale = WidthScale; -}()); diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass index ae07e7516..e7a834d45 100644 --- a/app/assets/stylesheets/application.sass +++ b/app/assets/stylesheets/application.sass @@ -5,3 +5,4 @@ @import 'custom_bootstrap/custom_bootstrap' @import 'overrides' @import 'graphs' +@import 'predictions' diff --git a/app/assets/stylesheets/custom_bootstrap/variables.sass b/app/assets/stylesheets/custom_bootstrap/variables.sass index fffafa099..f94f04106 100644 --- a/app/assets/stylesheets/custom_bootstrap/variables.sass +++ b/app/assets/stylesheets/custom_bootstrap/variables.sass @@ -13,6 +13,7 @@ $blue: #2f4365 $red: #8e4d43 $orange: #ffa500 $yellow: #b2935c +$white: #ffffff $body-bg: $beige $text-color: $brown diff --git a/app/assets/stylesheets/overrides.sass b/app/assets/stylesheets/overrides.sass index 11607abb3..bdeb64d0d 100644 --- a/app/assets/stylesheets/overrides.sass +++ b/app/assets/stylesheets/overrides.sass @@ -3,37 +3,18 @@ @import "custom_bootstrap/variables" // this padding needs to be done before the responsive stuff is imported body - // modifying this for our promotional banner. can be replaced after if - // needed. - // padding-top: $navbar-height + 15px padding-top: $navbar-height - -//@import "bootstrap/responsive" - // Font Awesome @import "font-awesome-sprockets" @import "font-awesome" -// Glyphicons -//@import "bootstrap/glyphicons" - - .list-inline > li.first padding-left: 0px h2 font-size: 150% - -//#subtitle -// color: lighten($brown, 30%) -// font-style: italic -// font-weight: normal -// margin-top: 0px -// padding-left: 1em -// padding-top: 0px - h3 font-size: 120% diff --git a/app/assets/stylesheets/predictions.sass b/app/assets/stylesheets/predictions.sass new file mode 100644 index 000000000..00be9af21 --- /dev/null +++ b/app/assets/stylesheets/predictions.sass @@ -0,0 +1,11 @@ +.predictions + .metric + height: 180px + border: 1px solid lighten($green, 20%) + background: $white + margin: 4px + strong + font-size: 250% + font-align: center + h3 + diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 3df1a7ca7..d3bd5dcf9 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -51,11 +51,7 @@ class CropsController < ApplicationController def show @crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id]) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) - - respond_to do |format| - format.html # show.html.haml - format.json { render json: @crop.to_json(crop_json_fields) } - end + respond_with(@crop) end def new @@ -106,8 +102,30 @@ class CropsController < ApplicationController respond_with @crop end + def sunniness + pie_chart_query 'sunniness' + end + + def planted_from + pie_chart_query 'planted_from' + end + + def harvested_for + @crop = Crop.find(params[:crop_id]) + render json: Harvest.joins(:plant_part).where(crop: @crop) + .group("plant_parts.name").count(:id) + end + private + def pie_chart_query(field) + @crop = Crop.find(params[:crop_id]) + render json: Planting.where(crop: @crop) + .where.not(field.to_sym => nil) + .where.not(field.to_sym => '') + .group(field.to_sym).count(:id) + end + def notifier case @crop.approval_status when "approved" diff --git a/app/models/csv_importer.rb b/app/models/csv_importer.rb index 66297b13c..f95fae8f2 100644 --- a/app/models/csv_importer.rb +++ b/app/models/csv_importer.rb @@ -64,7 +64,7 @@ class CsvImporter def cropbot @cropbot = Member.find_by!(login_name: 'cropbot') unless @cropbot @cropbot - rescue + rescue StandardError raise "cropbot account not found: run rake db:seed" end end diff --git a/app/views/crops/_predictions.html.haml b/app/views/crops/_predictions.html.haml index dbde9c783..74e8bafb1 100644 --- a/app/views/crops/_predictions.html.haml +++ b/app/views/crops/_predictions.html.haml @@ -1,29 +1,35 @@ -- unless crop.perennial.nil? - %p - #{crop.name} is - - if crop.perennial == true - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - a perennial crop - (living more than two years) - - elsif crop.perennial == false - = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do - an annual crop - (living and reproducing in a single year or less) +.predictions + - unless crop.perennial.nil? + .row + .col-md-12 + %p + #{crop.name} is + - if crop.perennial == true + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + a perennial crop + (living more than two years) + - elsif crop.perennial == false + = link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do + an annual crop + (living and reproducing in a single year or less) -- if crop.annual? && crop.median_lifespan.present? - %p - Median lifespan of #{crop.name} plants is - %strong= crop.median_lifespan - days + .row + - if crop.annual? && crop.median_lifespan.present? + .metric.col-md-3.col-xs-5 + %h3 Median lifespan + %strong= crop.median_lifespan + %span days -- if crop.median_days_to_first_harvest.present? - %p - First harvest expected - %strong= crop.median_days_to_first_harvest - days after planting + - if crop.median_days_to_first_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 First harvest expected + %strong= crop.median_days_to_first_harvest + %span days + after planting -- if crop.annual? && crop.median_days_to_last_harvest.present? - %p - Last harvest expected - %strong= crop.median_days_to_last_harvest - days after planting + - if crop.annual? && crop.median_days_to_last_harvest.present? + .metric.col-md-3.col-xs-5 + %h3 Last harvest expected + %strong= crop.median_days_to_last_harvest + %span days + after planting diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 2789d61ef..270c0db2b 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -23,37 +23,51 @@ .row .col-md-9 - - if member_signed_in? - = display_seed_availability(@current_member, @crop) - = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) + .row + .col-md-12 + - if member_signed_in? + = display_seed_availability(@current_member, @crop) + = link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug) - %h2 Predictions - = render 'predictions', crop: @crop + %h2 + - if !@crop.plantings.empty? + = @crop.name.titleize + has been planted + = pluralize(@crop.plantings.size, "time") + by #{ENV['GROWSTUFF_SITE_NAME']} members. + - else + Nobody is growing this yet. You could be the first! - %p= render 'crops/photos', crop: @crop - %h2 - - if !@crop.plantings.empty? - = @crop.name.titleize - has been planted - = pluralize(@crop.plantings.size, "time") - by #{ENV['GROWSTUFF_SITE_NAME']} members. - - else - Nobody is growing this yet. You could be the first! + .row + .col-md-12 + %h2 Predictions + = render 'predictions', crop: @crop - %h2 - Sunniness Chart + .row + .col-md-12 + %h2 Photos + %p= render 'crops/photos', crop: @crop + .row + .col-md-3 + %h2 Sunniness + = pie_chart crop_sunniness_path(@crop), legend: "bottom" - #sunchart + .col-md-3 + %h2 Planted from + = pie_chart crop_planted_from_path(@crop), legend: "bottom" + .col-md-3 + %h2 Harvested for + = pie_chart crop_harvested_for_path(@crop), legend: "bottom" - %h2 - Crop Map - %p - Only plantings by members who have set their locations are shown on this map. - - if current_member && current_member.location.blank? - = link_to "Set your location.", edit_member_registration_path - - #cropmap + .row + .col-md-12 + %h2 Crop Map + %p + Only plantings by members who have set their locations are shown on this map. + - if current_member && current_member.location.blank? + = link_to "Set your location.", edit_member_registration_path + #cropmap %a{ name: 'posts' } %h2 What people are saying about #{@crop.name.pluralize} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7fe945d8a..b90c025e7 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,6 @@ !!! 5 %html{ lang: "en", prefix: "og: http://ogp.me/ns#" } + = javascript_include_tag "application" = render partial: "layouts/meta" %body = render partial: "layouts/header" @@ -26,6 +27,4 @@ Javascripts \================================================== / Placed at the end of the document so the pages load faster - = javascript_include_tag "application" - != Growstuff::Application.config.analytics_code diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 000000000..4b828e80c --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. +Rails.application.config.assets.paths << Rails.root.join('node_modules') + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/config/routes.rb b/config/routes.rb index eb963311a..9d4e3fe60 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,9 @@ Growstuff::Application.routes.draw do get 'crops/search' => 'crops#search', as: 'crops_search' resources :crops do get 'photos' => 'photos#index' + get 'sunniness' => 'crops#sunniness' + get 'planted_from' => 'crops#planted_from' + get 'harvested_for' => 'crops#harvested_for' end resources :comments diff --git a/db/seeds.rb b/db/seeds.rb index 7df7e0e4c..be885a352 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -65,7 +65,7 @@ def load_test_users # rubocop:disable Metrics/AbcSize source_path = Rails.root.join('db', 'seeds') begin suburb_file = File.open("#{source_path}/suburbs.csv") - rescue + rescue StandardError puts "Warning: unable to open suburbs.csv" end diff --git a/spec/controllers/photo_associations_controller_spec.rb b/spec/controllers/photo_associations_controller_spec.rb index 75a04eedd..f021d5aa8 100644 --- a/spec/controllers/photo_associations_controller_spec.rb +++ b/spec/controllers/photo_associations_controller_spec.rb @@ -31,7 +31,7 @@ describe PhotoAssociationsController do expect do begin delete :destroy, valid_params - rescue + rescue StandardError nil end end.not_to change(photo.harvests, :count) diff --git a/spec/features/crops/crop_detail_page_spec.rb b/spec/features/crops/crop_detail_page_spec.rb index 7e927756d..1ee9bae80 100644 --- a/spec/features/crops/crop_detail_page_spec.rb +++ b/spec/features/crops/crop_detail_page_spec.rb @@ -181,6 +181,7 @@ feature "crop detail page", js: true do # 10 days to harvest FactoryBot.create(:harvest, harvested_at: 190.days.ago, crop: planting.crop, planting: FactoryBot.create(:planting, planted_at: 200.days.ago, crop: crop)) + planting.crop.update_medians end it "predicts harvest" do is_expected.to have_text("First harvest expected 20 days after planting") @@ -216,7 +217,8 @@ feature "crop detail page", js: true do end it "predicts lifespan" do - is_expected.to have_text "Median lifespan of #{crop.name} plants is 99 days" + is_expected.to have_text "Median lifespan" + is_expected.to have_text "99 days" end it "describes annual crops" do diff --git a/spec/javascripts/graphs/bar_group_spec.js b/spec/javascripts/graphs/bar_group_spec.js deleted file mode 100644 index a76992a6c..000000000 --- a/spec/javascripts/graphs/bar_group_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -(function() { - 'use strict'; - - /* - These tests are for the BarGroup object. - */ - - describe('when drawing the group of bars', function() { - var BarGroup; var subject; var bars; var data; - - beforeEach(function() { - BarGroup = growstuff.BarGroup; - - bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new BarGroup(data); - subject.render(d3.select('#jasmine_content').append('svg')); - }); - - it('draws a group', function() { - expect($('g.bar')).toExist(); - }); - - it('draws 2 bars', function() { - expect($('g.bar rect')).toHaveLength(2); - }); - - it('fills the bars with color', function() { - expect($('g.bar rect')).toHaveAttr('fill', 'steelblue'); - }); - - it('shows a tooltip on hover', function() { - var i; - - // get all of the title nodes for the bars - var barNodes = $('g.bar rect title'); - - for (i = 0; i < bars.length; i++) { - expect(barNodes[i].textContent) - .toBe('This value is ' + bars[i].value + '' + '.'); - } - }); - }); -}()); diff --git a/spec/javascripts/graphs/bar_label_group_spec.js b/spec/javascripts/graphs/bar_label_group_spec.js deleted file mode 100644 index 7cb6e5537..000000000 --- a/spec/javascripts/graphs/bar_label_group_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -(function() { - 'use strict'; - - /* - This file contains tests for the labels that get rendered next to each bar - */ - - describe('BarLabelGroup', function() { - var BarLabelGroup; var subject; var data; - - beforeEach(function() { - BarLabelGroup = growstuff.BarLabelGroup; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - }; - subject = new BarLabelGroup(data); - subject.render(d3.select('#jasmine_content').append('svg')); - }); - - it('draws a group for labels', function() { - expect($('g.bar-label')).toExist(); - }); - - it('draws 2 bar labels', function() { - expect($('g.bar-label text')).toHaveLength(2); - }); - - it('has text for 2 bar labels', function() { - // jquery jasmine appends text from all text elements - // into one string - expect($('g.bar-label text')).toHaveText('ShadeHalf Shade'); - }); - }); -}()); diff --git a/spec/javascripts/graphs/height_scale_spec.js b/spec/javascripts/graphs/height_scale_spec.js deleted file mode 100644 index 3a257400c..000000000 --- a/spec/javascripts/graphs/height_scale_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -(function() { - 'use strict'; - - /* - Tests for mapping the number of bars to the size of the svg - */ - - describe('HeightScale when specifying height', function() { - var data; var bars; var HeightScale; var subject; var mockD3; - - beforeEach(function() { - HeightScale = growstuff.HeightScale; - bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new HeightScale(data); - mockD3 = jasmine.createSpyObj('d3', ['domain', 'rangeRoundBands']); - mockD3.domain.and.returnValue(mockD3); - mockD3.rangeRoundBands.and.returnValue(mockD3); - spyOn(d3.scale, 'ordinal').and.returnValue(mockD3); - subject.render(); - }); - - it('calls the d3 range round bands function to draw the height', function() { - expect(mockD3.rangeRoundBands).toHaveBeenCalledWith([0, 400], 0.05, 0); - }); - }); -}()); diff --git a/spec/javascripts/graphs/horizontal_bar_graph_spec.js b/spec/javascripts/graphs/horizontal_bar_graph_spec.js deleted file mode 100644 index 16f35f018..000000000 --- a/spec/javascripts/graphs/horizontal_bar_graph_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -(function() { - 'use strict'; - - /* - Tests in this file are for the pieces of HorizontalBarGraph or - are more integration-y type tests that require the full graph. - */ - - describe('HorizontalBarGraph', function() { - var BarLabelGroup; var BarGroup; var subject; var data; - - beforeEach(function() { - var HorizontalBarGraph = growstuff.HorizontalBarGraph; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - bar_color: 'steelblue', - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - // left is used to shift the bars over so that there is - // room for the labels - margin: {top: 0, right: 0, bottom: 0, left: 100}, - }; - - subject = new HorizontalBarGraph(data); - BarGroup = growstuff.BarGroup; - BarLabelGroup = growstuff.BarLabelGroup; - expect(BarLabelGroup).toExist(); - spyOn(BarGroup.prototype, 'render').and.callThrough(); - spyOn(BarLabelGroup.prototype, 'render').and.callThrough(); - subject.render(d3.select($('#jasmine_content')[0])); - }); - - it('draws a graph', function() { - expect($('#jasmine_content svg')).toExist(); - }); - - it('draws a group for the whole graph', function() { - expect($('g.bar-graph')).toExist(); - }); - - it('draws a bar group', function() { - expect(BarGroup.prototype.render).toHaveBeenCalled(); - }); - - it('draws a group of bar labels', function() { - expect(BarLabelGroup.prototype.render).toHaveBeenCalled(); - }); - - it('has the expected width and height', function() { - var $svg = $('svg'); - var margin = data.margin; - expect($svg).toHaveAttr('width', (data.width.size + margin.left + margin.right) + ''); - expect($svg).toHaveAttr('height', (data.height.size + margin.top + margin.bottom) + ''); - }); - - it('draws the graph shifted to the right to accommodate for labels', function() { - expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)'); - }); - - it('on the x axis, draws at least one bar at max width less margin width', function() { - // because of domain and range mapping - expect('g.bar rect:eq(1)').toHaveAttr('width', '300' ); - }); - - it('on the y axis, all bars are the same height', function() { - expect('g.bar rect:eq(0)').toHaveAttr('height', '195'); - expect('g.bar rect:eq(1)').toHaveAttr('height', '195'); - }); - }); -}()); diff --git a/spec/javascripts/graphs/width_scale_spec.js b/spec/javascripts/graphs/width_scale_spec.js deleted file mode 100644 index d8e34234a..000000000 --- a/spec/javascripts/graphs/width_scale_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -(function() { - 'use strict'; - - /* - This file contains tests for the mapping the data values to - the length of a bar so that it is the correct size for the screen - */ - - describe('GraphScale, when specifying width', function() { - var data; var WidthScale; var subject; var mockD3; - - beforeEach(function() { - WidthScale = growstuff.WidthScale; - var bars = [ - {name: 'Shade', value: 0.2}, - {name: 'Half Shade', value: 0.5}, - ]; - data = { - bars: bars, - width: {size: 300, scale: 'linear'}, - height: {size: 400, scale: 'ordinal'}, - }; - - subject = new WidthScale(data, 'width'); - mockD3 = jasmine.createSpyObj('d3', ['domain', 'range', 'max']); - mockD3.domain.and.returnValue(mockD3); - mockD3.range.and.returnValue(mockD3); - spyOn(d3.scale, 'linear').and.returnValue(mockD3); - subject.render(); - }); - - it('gets the value of the longest bar', function() { - expect(subject.getMaxValue()).toEqual(0.5); - }); - - it('calls the d3 range function to draw the width', function() { - expect(mockD3.range).toHaveBeenCalledWith([0, 300]); - }); - }); -}());