Skip to content
Snippets Groups Projects
Commit 71d67e65 authored by Karlo Soriano's avatar Karlo Soriano Committed by karlo57
Browse files

Contributors graphs feature for GitLab

Created tests and refactored some code along the way

Added stat graph util spec, refactored code

finsihed up tests and refactors

finsihed up tests and refactors
parent b9d989dc
No related branches found
No related tags found
1 merge request!4212Contributors Graph
Showing
with 927 additions and 0 deletions
Loading
Loading
@@ -107,6 +107,12 @@ gem 'tinder', '~> 1.9.2'
# HipChat integration
gem "hipchat", "~> 0.9.0"
 
# d3
gem "d3_rails", "~> 3.1.4"
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
group :assets do
gem "sass-rails"
gem "coffee-rails"
Loading
Loading
@@ -177,6 +183,7 @@ group :development, :test do
gem 'poltergeist', '~> 1.3.0'
 
gem 'spork', '~> 1.0rc'
gem 'jasmine'
end
 
group :test do
Loading
Loading
Loading
Loading
@@ -69,6 +69,8 @@ GEM
celluloid (0.14.0)
timers (>= 1.0.0)
charlock_holmes (0.6.9.4)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
chosen-rails (0.9.8)
railties (~> 3.0)
thor (~> 0.14)
Loading
Loading
@@ -92,6 +94,8 @@ GEM
simplecov (>= 0.7)
thor
crack (0.3.2)
d3_rails (3.1.4)
railties (>= 3.1.0)
daemons (1.1.9)
database_cleaner (1.0.1)
debug_inspector (0.0.2)
Loading
Loading
@@ -216,6 +220,12 @@ GEM
multi_xml (>= 0.5.2)
httpauth (0.2.0)
i18n (0.6.1)
jasmine (1.3.2)
jasmine-core (~> 1.3.1)
rack (~> 1.0)
rspec (>= 1.3.1)
selenium-webdriver (>= 0.1.3)
jasmine-core (1.3.1)
journey (1.0.4)
jquery-atwho-rails (0.3.0)
jquery-rails (2.1.3)
Loading
Loading
@@ -392,6 +402,7 @@ GEM
rspec-mocks (~> 2.13.0)
ruby-progressbar (1.0.2)
rubyntlm (0.1.1)
rubyzip (0.9.9)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.2.9)
Loading
Loading
@@ -408,6 +419,11 @@ GEM
select2-rails (3.3.1)
sass-rails (>= 3.2)
thor (~> 0.14)
selenium-webdriver (2.32.1)
childprocess (>= 0.2.5)
multi_json (~> 1.0)
rubyzip
websocket (~> 1.0.4)
settingslogic (2.0.9)
sexp_processor (4.2.1)
shoulda-matchers (2.1.0)
Loading
Loading
@@ -482,6 +498,7 @@ GEM
uglifier (2.0.1)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
underscore-rails (1.4.4)
virtus (0.5.4)
backports (~> 2.6.1)
descendants_tracker (~> 0.0.1)
Loading
Loading
@@ -490,6 +507,7 @@ GEM
webmock (1.11.0)
addressable (>= 2.2.7)
crack (>= 0.3.2)
websocket (1.0.7)
xpath (2.0.0)
nokogiri (~> 1.3)
yajl-ruby (1.1.0)
Loading
Loading
@@ -510,6 +528,7 @@ DEPENDENCIES
coffee-rails
colored
coveralls
d3_rails (~> 3.1.4)
database_cleaner
devise
email_spec
Loading
Loading
@@ -536,6 +555,7 @@ DEPENDENCIES
haml-rails
hipchat (~> 0.9.0)
httparty
jasmine
jquery-atwho-rails (= 0.3.0)
jquery-rails (= 2.1.3)
jquery-turbolinks
Loading
Loading
@@ -586,4 +606,5 @@ DEPENDENCIES
tinder (~> 1.9.2)
turbolinks
uglifier
underscore-rails (~> 1.4.4)
webmock
Loading
Loading
@@ -27,3 +27,5 @@
//= require branch-graph
//= require ace-src-noconflict/ace
//= require_tree .
//= require d3
//= require underscore
class window.StatGraph
@log: {}
@get_log: ->
@log
@set_log: (data) ->
@log = data
class window.ContributorsStatGraph
init: (log) ->
@parsed_log = ContributorsStatGraphUtil.parse_log(log)
@set_current_field("commits")
total_commits = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field)
author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field)
@add_master_graph(total_commits)
@add_authors_graph(author_commits)
@change_date_header()
add_master_graph: (total_data) ->
@master_graph = new ContributorsMasterGraph(total_data)
@master_graph.draw()
add_authors_graph: (author_data) ->
@authors = []
_.each(author_data, (d) =>
author_header = @create_author_header(d)
$(".contributors-list").append(author_header)
@authors[d.author] = author_graph = new ContributorsAuthorGraph(d.dates)
author_graph.draw()
)
format_author_commit_info: (author) ->
author.commits + " commits " + author.additions + " ++ / " + author.deletions + " --"
create_author_header: (author) ->
list_item = $('<li/>', {
class: 'person'
style: 'display: block;'
})
author_name = $('<h4>' + author.author + '</h4>')
author_commit_info_span = $('<span/>', {
class: 'commits'
})
author_commit_info = @format_author_commit_info(author)
author_commit_info_span.text(author_commit_info)
list_item.append(author_name)
list_item.append(author_commit_info_span)
list_item
redraw_master: ->
total_data = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field)
@master_graph.set_data(total_data)
@master_graph.redraw()
redraw_authors: ->
$("ol").html("")
x_domain = ContributorsGraph.prototype.x_domain
author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field, x_domain)
_.each(author_commits, (d) =>
@redraw_author_commit_info(d)
$(@authors[d.author].list_item).appendTo("ol")
@authors[d.author].set_data(d.dates)
@authors[d.author].redraw()
)
set_current_field: (field) ->
@field = field
change_date_header: ->
x_domain = ContributorsGraph.prototype.x_domain
print_date_format = d3.time.format("%B %e %Y");
print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]);
$("#date_header").text(print);
redraw_author_commit_info: (author) ->
author_list_item = $(@authors[author.author].list_item)
author_commit_info = @format_author_commit_info(author)
author_list_item.find("span").text(author_commit_info)
\ No newline at end of file
class window.ContributorsGraph
MARGIN:
top: 20
right: 20
bottom: 30
left: 50
x_domain: null
y_domain: null
dates: []
@set_x_domain: (data) =>
@prototype.x_domain = data
@set_y_domain: (data) =>
@prototype.y_domain = [0, d3.max(data, (d) ->
d.commits = d.commits ? d.additions ? d.deletions
)]
@init_x_domain: (data) =>
@prototype.x_domain = d3.extent(data, (d) ->
d.date
)
@init_y_domain: (data) =>
@prototype.y_domain = [0, d3.max(data, (d) ->
d.commits = d.commits ? d.additions ? d.deletions
)]
@init_domain: (data) =>
@init_x_domain(data)
@init_y_domain(data)
@set_dates: (data) =>
@prototype.dates = data
set_x_domain: ->
@x.domain(@x_domain)
set_y_domain: ->
@y.domain(@y_domain)
set_domain: ->
@set_x_domain()
@set_y_domain()
create_scale: (width, height) ->
@x = d3.time.scale().range([0, width]).clamp(true)
@y = d3.scale.linear().range([height, 0]).nice()
draw_x_axis: ->
@svg.append("g").attr("class", "x axis").attr("transform", "translate(0, #{@height})")
.call(@x_axis);
draw_y_axis: ->
@svg.append("g").attr("class", "y axis").call(@y_axis)
set_data: (data) ->
@data = data
class window.ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
@width = 1100
@height = 125
@x = null
@y = null
@x_axis = null
@y_axis = null
@area = null
@svg = null
@brush = null
@x_max_domain = null
process_dates: (data) ->
dates = @get_dates(data)
@parse_dates(data)
ContributorsGraph.set_dates(dates)
get_dates: (data) ->
_.pluck(data, 'date')
parse_dates: (data) ->
parseDate = d3.time.format("%Y-%m-%d").parse
data.forEach((d) ->
d.date = parseDate(d.date)
)
create_scale: ->
super @width, @height
create_axes: ->
@x_axis = d3.svg.axis().scale(@x).orient("bottom")
@y_axis = d3.svg.axis().scale(@y).orient("left")
create_svg: ->
@svg = d3.select("#contributors-master").append("svg")
.attr("width", @width + @MARGIN.left + @MARGIN.right)
.attr("height", @height + @MARGIN.top + @MARGIN.bottom)
.attr("class", "tint-box")
.append("g")
.attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")")
create_area: (x, y) ->
@area = d3.svg.area().x((d) ->
x(d.date)
).y0(@height).y1((d) ->
y(d.commits = d.commits ? d.additions ? d.deletions)
).interpolate("basis")
create_brush: ->
@brush = d3.svg.brush().x(@x).on("brushend", @update_content);
draw_path: (data) ->
@svg.append("path").datum(data).attr("class", "area").attr("d", @area);
add_brush: ->
@svg.append("g").attr("class", "selection").call(@brush).selectAll("rect").attr("height", @height);
update_content: =>
ContributorsGraph.set_x_domain(if @brush.empty() then @x_max_domain else @brush.extent())
$("#brush_change").trigger('change')
draw: ->
@process_dates(@data)
@create_scale()
@create_axes()
ContributorsGraph.init_domain(@data)
@x_max_domain = @x_domain
@set_domain()
@create_area(@x, @y)
@create_svg()
@create_brush()
@draw_path(@data)
@draw_x_axis()
@draw_y_axis()
@add_brush()
redraw: ->
@process_dates(@data)
ContributorsGraph.set_y_domain(@data)
@set_y_domain()
@svg.select("path").datum(@data)
@svg.select("path").attr("d", @area)
@svg.select(".y.axis").call(@y_axis)
class window.ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = 490
@height = 130
@x = null
@y = null
@x_axis = null
@y_axis = null
@area = null
@svg = null
@list_item = null
create_scale: ->
super @width, @height
create_axes: ->
@x_axis = d3.svg.axis().scale(@x).orient("bottom").tickFormat(d3.time.format("%m/%d"));
@y_axis = d3.svg.axis().scale(@y).orient("left")
create_area: (x, y) ->
@area = d3.svg.area().x((d) ->
parseDate = d3.time.format("%Y-%m-%d").parse
x(parseDate(d))
).y0(@height).y1((d) =>
if @data[d]? then y(@data[d]) else y(0)
).interpolate("basis")
create_svg: ->
@list_item = d3.selectAll(".person")[0].pop()
@svg = d3.select(@list_item).append("svg")
.attr("width", @width + @MARGIN.left + @MARGIN.right)
.attr("height", @height + @MARGIN.top + @MARGIN.bottom)
.attr("class", "spark")
.append("g")
.attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")")
draw_path: (data) ->
@svg.append("path").datum(data).attr("class", "area-contributor").attr("d", @area);
draw: ->
@create_scale()
@create_axes()
@set_domain()
@create_area(@x, @y)
@create_svg()
@draw_path(@dates)
@draw_x_axis()
@draw_y_axis()
redraw: ->
@set_domain()
@svg.select("path").datum(@dates)
@svg.select("path").attr("d", @area)
@svg.select(".x.axis").call(@x_axis)
@svg.select(".y.axis").call(@y_axis)
window.ContributorsStatGraphUtil =
parse_log: (log) ->
total = {}
by_author = {}
for entry in log
@add_date(entry.date, total) unless total[entry.date]?
@add_author(entry.author, by_author) unless by_author[entry.author]?
@add_date(entry.date, by_author[entry.author]) unless by_author[entry.author][entry.date]
@store_data(entry, total[entry.date], by_author[entry.author][entry.date])
total = _.toArray(total)
by_author = _.toArray(by_author)
total: total, by_author: by_author
add_date: (date, collection) ->
collection[date] = {}
collection[date].date = date
add_author: (author, by_author) ->
by_author[author] = {}
by_author[author].author = author
store_data: (entry, total, by_author) ->
@store_commits(total, by_author)
@store_additions(entry, total, by_author)
@store_deletions(entry, total, by_author)
store_commits: (total, by_author) ->
@add(total, "commits", 1)
@add(by_author, "commits", 1)
add: (collection, field, value) ->
collection[field] ?= 0
collection[field] += value
store_additions: (entry, total, by_author) ->
entry.additions ?= 0
@add(total, "additions", entry.additions)
@add(by_author, "additions", entry.additions)
store_deletions: (entry, total, by_author) ->
entry.deletions ?= 0
@add(total, "deletions", entry.deletions)
@add(by_author, "deletions", entry.deletions)
get_total_data: (parsed_log, field) ->
log = parsed_log.total
total_data = @pick_field(log, field)
_.sortBy(total_data, (d) ->
d.date
)
pick_field: (log, field) ->
total_data = []
_.each(log, (d) ->
total_data.push(_.pick(d, [field, 'date']))
)
total_data
get_author_data: (parsed_log, field, date_range = null) ->
log = parsed_log.by_author
author_data = []
_.each(log, (log_entry) =>
parsed_log_entry = @parse_log_entry(log_entry, field, date_range)
if not _.isEmpty(parsed_log_entry.dates)
author_data.push(parsed_log_entry)
)
_.sortBy(author_data, (d) ->
d[field]
).reverse()
parse_log_entry: (log_entry, field, date_range) ->
parsed_entry = {}
parsed_entry.author = log_entry.author
parsed_entry.dates = {}
parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0
_.each(_.omit(log_entry, 'author'), (value, key) =>
if @in_range(value.date, date_range)
parsed_entry.dates[value.date] = value[field]
parsed_entry.commits += value.commits
parsed_entry.additions += value.additions
parsed_entry.deletions += value.deletions
)
return parsed_entry
in_range: (date, date_range) ->
if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
true
else
false
\ No newline at end of file
Loading
Loading
@@ -37,6 +37,7 @@
@import "sections/wiki.scss";
@import "sections/wall.scss";
@import "sections/dashboard.scss";
@import "sections/stat_graph.scss";
 
@import "highlight/white.scss";
@import "highlight/dark.scss";
Loading
Loading
.tint-box {
border-radius: 6px;
background: #f3f3f3;
position: relative;
margin-bottom: 10px;
}
.area {
fill: #1db34f;
fill-opacity: 0.5;
}
.axis {
fill: #aaa;
font-size: 10px;
}
#contributors .person {
-moz-box-sizing: border-box;
box-sizing: border-box;
float: left;
border-radius: 2px;
margin: 10px;
border: 1px solid #ddd;
}
.contributors-list {
margin: 0 0 10px 0;
list-style: none;
padding: 0;
}
#contributors .person .spark {
display: block;
background: #f7f7f7;
}
#contributors .person .area-contributor {
fill: #f17f49;
}
.selection rect {
fill: #333;
fill-opacity: 0.1;
stroke: #333;
stroke-width: 1px;
stroke-opacity: 0.4;
shape-rendering: crispedges;
stroke-dasharray: 3 3;
}
.right{
float: right;
display: inline-block;
margin-top: 5px;
}
class StatGraphController < ProjectResourceController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
def show
@repo = @project.repository
@stats = Gitlab::GitStats.new(@repo.raw, @repo.root_ref)
@log = @stats.parsed_log.to_json
end
end
\ No newline at end of file
Loading
Loading
@@ -11,6 +11,8 @@
= link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
= nav_link(controller: %w(graph)) do
= link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
= nav_link(controller: %w(stat_graph)) do
= link_to "Graphs", project_stat_graph_path(@project, @ref || @repository.root_ref)
 
- if @project.issues_enabled
= nav_link(controller: %w(issues milestones labels)) do
Loading
Loading
.header.clearfix
.right
%select
%option{:value => "commits"} Commits
%option{:value => "additions"} Additions
%option{:value => "deletions"} Deletions
%h3#date_header
%input#brush_change{:type => "hidden"}
.graphs
#contributors-master
#contributors.clearfix
%ol.contributors-list.clearfix
:javascript
controller = new ContributorsStatGraph
controller.init(#{@log})
$("select").change( function () {
var field = $(this).val()
controller.set_current_field(field)
controller.redraw_master()
controller.redraw_authors()
})
$("#brush_change").change( function () {
controller.change_date_header()
controller.redraw_authors()
})
\ No newline at end of file
Loading
Loading
@@ -190,6 +190,7 @@ Gitlab::Application.routes.draw do
resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/}
resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
resources :stat_graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
 
scope module: :projects do
Loading
Loading
require 'gitlab/git_stats_log_parser'
module Gitlab
class GitStats
attr_accessor :repo, :ref
def initialize repo, ref
@repo, @ref = repo, ref
end
def log
args = ['--format=%aN%x0a%ad', '--date=short', '--shortstat', '--no-merges']
repo.git.run(nil, 'log', nil, {}, args)
end
def parsed_log
LogParser.parse_log(log)
end
end
end
class LogParser
#Parses the log file into a collection of commits
#Data model: {author, date, additions, deletions}
def self.parse_log log_from_git
log = log_from_git.split("\n")
i = 0
collection = []
entry = {}
while i <= log.size do
pos = i % 4
case pos
when 0
unless i == 0
collection.push(entry)
entry = {}
end
entry[:author] = log[i].to_s
when 1
entry[:date] = log[i].to_s
when 3
changes = log[i].split(",")
entry[:additions] = changes[1].to_i unless changes[1].nil?
entry[:deletions] = changes[2].to_i unless changes[2].nil?
end
i += 1
end
collection
end
end
\ No newline at end of file
describe("ContributorsGraph", function () {
describe("#set_x_domain", function () {
it("set the x_domain", function () {
ContributorsGraph.set_x_domain(20)
expect(ContributorsGraph.prototype.x_domain).toEqual(20)
})
})
describe("#set_y_domain", function () {
it("sets the y_domain", function () {
ContributorsGraph.set_y_domain([{commits: 30}])
expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30])
})
})
describe("#init_x_domain", function () {
it("sets the initial x_domain", function () {
ContributorsGraph.init_x_domain([{date: "2013-01-31"}, {date: "2012-01-31"}])
expect(ContributorsGraph.prototype.x_domain).toEqual(["2012-01-31", "2013-01-31"])
})
})
describe("#init_y_domain", function () {
it("sets the initial y_domain", function () {
ContributorsGraph.init_y_domain([{commits: 30}])
expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30])
})
})
describe("#init_domain", function () {
it("calls init_x_domain and init_y_domain", function () {
spyOn(ContributorsGraph, "init_x_domain")
spyOn(ContributorsGraph, "init_y_domain")
ContributorsGraph.init_domain()
expect(ContributorsGraph.init_x_domain).toHaveBeenCalled()
expect(ContributorsGraph.init_y_domain).toHaveBeenCalled()
})
})
describe("#set_dates", function () {
it("sets the dates", function () {
ContributorsGraph.set_dates("2013-12-01")
expect(ContributorsGraph.prototype.dates).toEqual("2013-12-01")
})
})
describe("#set_x_domain", function () {
it("sets the instance's x domain using the prototype's x_domain", function () {
ContributorsGraph.prototype.x_domain = 20
var instance = new ContributorsGraph()
instance.x = d3.time.scale().range([0, 100]).clamp(true)
spyOn(instance.x, 'domain')
instance.set_x_domain()
expect(instance.x.domain).toHaveBeenCalledWith(20)
})
})
describe("#set_y_domain", function () {
it("sets the instance's y domain using the prototype's y_domain", function () {
ContributorsGraph.prototype.y_domain = 30
var instance = new ContributorsGraph()
instance.y = d3.scale.linear().range([100, 0]).nice()
spyOn(instance.y, 'domain')
instance.set_y_domain()
expect(instance.y.domain).toHaveBeenCalledWith(30)
})
})
describe("#set_domain", function () {
it("calls set_x_domain and set_y_domain", function () {
var instance = new ContributorsGraph()
spyOn(instance, 'set_x_domain')
spyOn(instance, 'set_y_domain')
instance.set_domain()
expect(instance.set_x_domain).toHaveBeenCalled()
expect(instance.set_y_domain).toHaveBeenCalled()
})
})
describe("#set_data", function () {
it("sets the data", function () {
var instance = new ContributorsGraph()
instance.set_data("20")
expect(instance.data).toEqual("20")
})
})
})
describe("ContributorsMasterGraph", function () {
describe("#process_dates", function () {
it("gets and parses dates", function () {
var graph = new ContributorsMasterGraph()
var data = 'random data here'
spyOn(graph, 'parse_dates')
spyOn(graph, 'get_dates').andReturn("get")
spyOn(ContributorsGraph,'set_dates').andCallThrough()
graph.process_dates(data)
expect(graph.parse_dates).toHaveBeenCalledWith(data)
expect(graph.get_dates).toHaveBeenCalledWith(data)
expect(ContributorsGraph.set_dates).toHaveBeenCalledWith("get")
})
})
describe("#get_dates", function () {
it("plucks the date field from data collection", function () {
var graph = new ContributorsMasterGraph()
var data = [{date: "2013-01-01"}, {date: "2012-12-15"}]
expect(graph.get_dates(data)).toEqual(["2013-01-01", "2012-12-15"])
})
})
describe("#parse_dates", function () {
it("parses the dates", function () {
var graph = new ContributorsMasterGraph()
var parseDate = d3.time.format("%Y-%m-%d").parse
var data = [{date: "2013-01-01"}, {date: "2012-12-15"}]
var correct = [{date: parseDate(data[0].date)}, {date: parseDate(data[1].date)}]
graph.parse_dates(data)
expect(data).toEqual(correct)
})
})
})
describe("ContributorsStatGraphUtil", function () {
describe("#parse_log", function () {
it("returns a correctly parsed log", function () {
var fake_log = [
{author: "Karlo Soriano", date: "2013-05-09", additions: 471},
{author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1},
{author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3},
{author: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3}]
var correct_parsed_log = {
total: [
{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
{date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
by_author:
[
{
author: "Karlo Soriano",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
},
{
author: "Dmitriy Zaporozhets",
"2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
}
]
}
expect(ContributorsStatGraphUtil.parse_log(fake_log)).toEqual(correct_parsed_log)
})
})
describe("#store_data", function () {
var fake_entry = {author: "Karlo Soriano", date: "2013-05-09", additions: 471}
var fake_total = {}
var fake_by_author = {}
it("calls #store_commits", function () {
spyOn(ContributorsStatGraphUtil, 'store_commits')
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.store_commits).toHaveBeenCalled()
})
it("calls #store_additions", function () {
spyOn(ContributorsStatGraphUtil, 'store_additions')
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.store_additions).toHaveBeenCalled()
})
it("calls #store_deletions", function () {
spyOn(ContributorsStatGraphUtil, 'store_deletions')
ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.store_deletions).toHaveBeenCalled()
})
})
describe("#store_commits", function () {
var fake_total = "fake_total"
var fake_by_author = "fake_by_author"
it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
spyOn(ContributorsStatGraphUtil, 'add')
ContributorsStatGraphUtil.store_commits(fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "commits", 1], ["fake_by_author", "commits", 1]])
})
})
describe("#add", function () {
it("adds 1 to current test_field in collection", function () {
var fake_collection = {test_field: 10}
ContributorsStatGraphUtil.add(fake_collection, "test_field", 1)
expect(fake_collection.test_field).toEqual(11)
})
it("inits and adds 1 if test_field in collection is not defined", function () {
var fake_collection = {}
ContributorsStatGraphUtil.add(fake_collection, "test_field", 1)
expect(fake_collection.test_field).toEqual(1)
})
})
describe("#store_additions", function () {
var fake_entry = {additions: 10}
var fake_total= "fake_total"
var fake_by_author = "fake_by_author"
it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
spyOn(ContributorsStatGraphUtil, 'add')
ContributorsStatGraphUtil.store_additions(fake_entry, fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "additions", 10], ["fake_by_author", "additions", 10]])
})
})
describe("#store_deletions", function () {
var fake_entry = {deletions: 10}
var fake_total= "fake_total"
var fake_by_author = "fake_by_author"
it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
spyOn(ContributorsStatGraphUtil, 'add')
ContributorsStatGraphUtil.store_deletions(fake_entry, fake_total, fake_by_author)
expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "deletions", 10], ["fake_by_author", "deletions", 10]])
})
})
describe("#add_date", function () {
it("adds a date field to the collection", function () {
var fake_date = "2013-10-02"
var fake_collection = {}
ContributorsStatGraphUtil.add_date(fake_date, fake_collection)
expect(fake_collection[fake_date].date).toEqual("2013-10-02")
})
})
describe("#add_author", function () {
it("adds an author field to the collection", function () {
var fake_author = "Author"
var fake_collection = {}
ContributorsStatGraphUtil.add_author(fake_author, fake_collection)
expect(fake_collection[fake_author].author).toEqual("Author")
})
})
describe("#get_total_data", function () {
it("returns the collection sorted via specified field", function () {
var fake_parsed_log = {
total: [{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
{date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
by_author:[
{
author: "Karlo Soriano",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
},
{
author: "Dmitriy Zaporozhets",
"2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
}
]};
var correct_total_data = [{date: "2013-05-08", commits: 3},
{date: "2013-05-09", commits: 1}];
expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, "commits")).toEqual(correct_total_data)
})
})
describe("#pick_field", function () {
it("returns the collection with only the specified field and date", function () {
var fake_parsed_log_total = [{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
{date: "2013-05-08", additions: 54, deletions: 7, commits: 3}];
ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits")
var correct_pick_field_data = [{date: "2013-05-09", commits: 1},{date: "2013-05-08", commits: 3}];
expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits")).toEqual(correct_pick_field_data)
})
})
describe("#get_author_data", function () {
it("returns the log by author sorted by specified field", function () {
var fake_parsed_log = {
total: [{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
{date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
by_author:[
{
author: "Karlo Soriano",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
},
{
author: "Dmitriy Zaporozhets",
"2013-05-08": {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
}
]}
var correct_author_data = [{author:"Dmitriy Zaporozhets",dates:{"2013-05-08":3},deletions:7,additions:54,"commits":3},
{author:"Karlo Soriano",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}]
expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, "commits")).toEqual(correct_author_data)
})
})
describe("#parse_log_entry", function () {
it("adds the corresponding info from the log entry to the author", function () {
var fake_log_entry = { author: "Karlo Soriano",
"2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
}
var correct_parsed_log = {author:"Karlo Soriano",dates:{"2013-05-09":1},deletions:0,additions:471,commits:1}
expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual(correct_parsed_log)
})
})
describe("#in_range", function () {
var date = "2013-05-09"
it("returns true if date_range is null", function () {
expect(ContributorsStatGraphUtil.in_range(date, null)).toEqual(true)
})
it("returns true if date is in range", function () {
var date_range = [new Date("2013-01-01"), new Date("2013-12-12")]
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(true)
})
it("returns false if date is not in range", function () {
var date_range = [new Date("1999-12-01"), new Date("2000-12-01")]
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(false)
})
})
})
\ No newline at end of file
describe("StatGraph", function () {
describe("#get_log", function () {
it("returns log", function () {
StatGraph.log = "test";
expect(StatGraph.get_log()).toBe("test");
});
});
describe("#set_log", function () {
it("sets the log", function () {
StatGraph.set_log("test");
expect(StatGraph.log).toBe("test");
})
})
});
\ No newline at end of file
# src_files
#
# Return an array of filepaths relative to src_dir to include before jasmine specs.
# Default: []
#
# EXAMPLE:
#
# src_files:
# - lib/source1.js
# - lib/source2.js
# - dist/**/*.js
#
src_files:
- assets/application.js
# stylesheets
#
# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
# Default: []
#
# EXAMPLE:
#
# stylesheets:
# - css/style.css
# - stylesheets/*.css
#
stylesheets:
- stylesheets/**/*.css
# helpers
#
# Return an array of filepaths relative to spec_dir to include before jasmine specs.
# Default: ["helpers/**/*.js"]
#
# EXAMPLE:
#
# helpers:
# - helpers/**/*.js
#
helpers:
- helpers/**/*.js
# spec_files
#
# Return an array of filepaths relative to spec_dir to include.
# Default: ["**/*[sS]pec.js"]
#
# EXAMPLE:
#
# spec_files:
# - **/*[sS]pec.js
#
spec_files:
- '**/*[sS]pec.js'
# src_dir
#
# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
# Default: project root
#
# EXAMPLE:
#
# src_dir: public
#
src_dir:
# spec_dir
#
# Spec directory path. Your spec_files must be returned relative to this path.
# Default: spec/javascripts
#
# EXAMPLE:
#
# spec_dir: spec/javascripts
#
spec_dir: spec/javascripts
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment