First Initial

This commit is contained in:
Nakorn Rientrakrunchai
2020-02-20 15:02:39 +07:00
commit 8b98125e49
3048 changed files with 760804 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
describe 'Morris.Area', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
lineColors: [ '#0b62a4', '#7a92a3']
gridLineColor: '#aaa'
xkey: 'x'
ykeys: ['y']
labels: ['Y']
it 'should contain a line path for each line', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain a path with stroke-width 0 for each line', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain 5 grid lines', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
it 'should contain 9 text elements', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("text").size().should.equal 9
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['Y']
lineColors: [ '#0b62a4', '#7a92a3']
lineWidth: 3
pointWidths: [5]
pointStrokeColors: ['#ffffff']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
it 'should not be cumulative if behaveLikeLine', ->
chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
chart.cumulative.should.equal false
it 'should have a line with transparent fill if behaveLikeLine', ->
chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
$('#graph').find("path[fill-opacity='0.8']").size().should.equal 1
it 'should not have a line with transparent fill', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[fill-opacity='0.8']").size().should.equal 0
it 'should have a line with the fill of a modified line color', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[fill='#0b62a4']").size().should.equal 0
$('#graph').find("path[fill='#7a92a3']").size().should.equal 0

View File

@@ -0,0 +1,50 @@
describe 'Morris.Bar', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
it 'should contain a rect for each bar', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect").size().should.equal 4
it 'should contain 5 grid lines', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("path").size().should.equal 5
it 'should contain 7 text elements', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text").size().should.equal 7
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
barColors: [ '#0b62a4', '#7a92a3']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
it 'should have a bar with the first default color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[fill='#0b62a4']").size().should.equal 2
it 'should have a bar with stroke width 0', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[stroke-width='0']").size().should.equal 4
it 'should have text with configured fill color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text[fill='#888888']").size().should.equal 7
it 'should have text with configured font size', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text[font-size='12px']").size().should.equal 7

View File

@@ -0,0 +1,36 @@
describe 'Morris.Bar#colorFor', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
it 'should fetch colours from an array', ->
chart = Morris.Bar $.extend {}, defaults, barColors: ['#f00', '#0f0', '#00f']
chart.colorFor(chart.data[0], 0, 'bar').should.equal '#f00'
chart.colorFor(chart.data[0], 0, 'hover').should.equal '#f00'
chart.colorFor(chart.data[0], 1, 'bar').should.equal '#0f0'
chart.colorFor(chart.data[0], 1, 'hover').should.equal '#0f0'
chart.colorFor(chart.data[0], 2, 'bar').should.equal '#00f'
chart.colorFor(chart.data[0], 2, 'hover').should.equal '#00f'
chart.colorFor(chart.data[0], 3, 'bar').should.equal '#f00'
chart.colorFor(chart.data[0], 4, 'hover').should.equal '#0f0'
it 'should defer to a callback', ->
stub = sinon.stub().returns '#f00'
chart = Morris.Bar $.extend {}, defaults, barColors: stub
stub.reset()
chart.colorFor(chart.data[0], 0, 'bar')
stub.should.have.been.calledWith(
{x:0, y:2, label:'foo'},
{index:0, key:'y', label:'Y'},
'bar')
chart.colorFor(chart.data[0], 1, 'hover')
stub.should.have.been.calledWith(
{x:0, y:3, label:'foo'},
{index:1, key:'z', label:'Z'},
'hover')

View File

@@ -0,0 +1,38 @@
describe '#commas', ->
it 'should insert commas into long numbers', ->
# zero
Morris.commas(0).should.equal("0")
# positive integers
Morris.commas(1).should.equal("1")
Morris.commas(12).should.equal("12")
Morris.commas(123).should.equal("123")
Morris.commas(1234).should.equal("1,234")
Morris.commas(12345).should.equal("12,345")
Morris.commas(123456).should.equal("123,456")
Morris.commas(1234567).should.equal("1,234,567")
# negative integers
Morris.commas(-1).should.equal("-1")
Morris.commas(-12).should.equal("-12")
Morris.commas(-123).should.equal("-123")
Morris.commas(-1234).should.equal("-1,234")
Morris.commas(-12345).should.equal("-12,345")
Morris.commas(-123456).should.equal("-123,456")
Morris.commas(-1234567).should.equal("-1,234,567")
# positive decimals
Morris.commas(1.2).should.equal("1.2")
Morris.commas(12.34).should.equal("12.34")
Morris.commas(123.456).should.equal("123.456")
Morris.commas(1234.56).should.equal("1,234.56")
# negative decimals
Morris.commas(-1.2).should.equal("-1.2")
Morris.commas(-12.34).should.equal("-12.34")
Morris.commas(-123.456).should.equal("-123.456")
Morris.commas(-1234.56).should.equal("-1,234.56")
# null
Morris.commas(null).should.equal('-')

View File

@@ -0,0 +1,61 @@
describe 'Morris.Donut', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [ {label: 'Jam', value: 25 },
{label: 'Frosted', value: 40 },
{label: 'Custard', value: 25 },
{label: 'Sugar', value: 10 } ]
formatter: (y) -> "#{y}%"
it 'should contain 2 paths for each segment', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path").size().should.equal 8
it 'should contain 2 text elements for the label', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text").size().should.equal 2
describe 'svg attributes', ->
defaults =
defaults =
element: 'graph'
data: [ {label: 'Jam', value: 25 },
{label: 'Frosted', value: 40 },
{label: 'Custard', value: 25 },
{label: 'Sugar', value: 10 } ]
formatter: (y) -> "#{y}%"
colors: [ '#0B62A4', '#3980B5', '#679DC6', '#95BBD7']
it 'should have a label with font size 15', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-size='15px']").size().should.equal 1
it 'should have a label with font size 14', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-size='14px']").size().should.equal 1
it 'should have a label with font-weight 800', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-weight='800']").size().should.equal 1
it 'should have 1 paths with fill of first color', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[fill='#0b62a4']").size().should.equal 1
it 'should have 1 paths with stroke of first color', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should have a path with white stroke', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke='#ffffff']").size().should.equal 4
it 'should have a path with stroke-width 3', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke-width='3']").size().should.equal 4
it 'should have a path with stroke-width 2', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke-width='2']").size().should.equal 4

View File

@@ -0,0 +1,25 @@
describe 'Morris.Grid#autoGridLines', ->
beforeEach ->
@subject = Morris.Grid.prototype.autoGridLines
it 'should draw at fixed intervals', ->
@subject(0, 4, 5).should.deep.equal [0, 1, 2, 3, 4]
@subject(0, 400, 5).should.deep.equal [0, 100, 200, 300, 400]
it 'should pick intervals that show significant numbers', ->
@subject(102, 499, 5).should.deep.equal [100, 200, 300, 400, 500]
it 'should draw zero when it falls within [ymin..ymax]', ->
@subject(-100, 300, 5).should.deep.equal [-100, 0, 100, 200, 300]
@subject(-50, 350, 5).should.deep.equal [-125, 0, 125, 250, 375]
@subject(-400, 400, 5).should.deep.equal [-400, -200, 0, 200, 400]
@subject(100, 500, 5).should.deep.equal [100, 200, 300, 400, 500]
@subject(-500, -100, 5).should.deep.equal [-500, -400, -300, -200, -100]
it 'should generate decimal labels to 2 significant figures', ->
@subject(0, 1, 5).should.deep.equal [0, 0.25, 0.5, 0.75, 1]
@subject(0.1, 0.5, 5).should.deep.equal [0.1, 0.2, 0.3, 0.4, 0.5]
it 'should use integer intervals for intervals larger than 1', ->
@subject(0, 9, 5).should.deep.equal [0, 3, 6, 9, 12]

View File

@@ -0,0 +1,208 @@
describe 'Morris.Grid#setData', ->
it 'should not alter user-supplied data', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
expected_data = [{x: 1, y: 1}, {x: 2, y: 2}]
Morris.Line
element: 'graph'
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
my_data.should.deep.equal expected_data
describe 'ymin/ymax', ->
beforeEach ->
@defaults =
element: 'graph'
xkey: 'x'
ykeys: ['y', 'z']
labels: ['y', 'z']
it 'should use a user-specified minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 1}]
ymin: 10
ymax: 20
line.ymin.should.equal 10
line.ymax.should.equal 20
describe 'auto', ->
it 'should automatically calculate the minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto'
ymax: 'auto'
line.ymin.should.equal 10
line.ymax.should.equal 15
it 'should automatically calculate the minimum and maximum value given no y data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto'
ymax: 'auto'
line.ymin.should.equal 0
line.ymax.should.equal 1
describe 'auto [n]', ->
it 'should automatically calculate the minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto 11'
ymax: 'auto 13'
line.ymin.should.equal 10
line.ymax.should.equal 15
it 'should automatically calculate the minimum and maximum value given no data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto 11'
ymax: 'auto 13'
line.ymin.should.equal 11
line.ymax.should.equal 13
it 'should use a user-specified minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto 5'
ymax: 'auto 20'
line.ymin.should.equal 5
line.ymax.should.equal 20
it 'should use a user-specified minimum and maximum value given no data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto 5'
ymax: 'auto 20'
line.ymin.should.equal 5
line.ymax.should.equal 20
describe 'xmin/xmax', ->
it 'should calculate the horizontal range', ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: 2}, {x: 1, y: 1}, {x: 4, y: 4}, {x: 3, y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.xmin.should == 1
line.xmax.should == 4
it "should pad the range if there's only one data point", ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: 2}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.xmin.should == 1
line.xmax.should == 3
describe 'sorting', ->
it 'should sort data when parseTime is true', ->
line = Morris.Line
element: 'graph'
data: [
{x: '2012 Q1', y: 2},
{x: '2012 Q3', y: 1},
{x: '2012 Q4', y: 4},
{x: '2012 Q2', y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.map((row) -> row.label).should.deep.equal ['2012 Q1', '2012 Q2', '2012 Q3', '2012 Q4']
it 'should not sort data when parseTime is false', ->
line = Morris.Line
element: 'graph'
data: [{x: 1, y: 2}, {x: 4, y: 1}, {x: 3, y: 4}, {x: 2, y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
parseTime: false
line.data.map((row) -> row.label).should.deep.equal [1, 4, 3, 2]
describe 'timestamp data', ->
it 'should generate default labels for timestamp x-values', ->
d = [
new Date 2012, 0, 1
new Date 2012, 0, 2
new Date 2012, 0, 3
new Date 2012, 0, 4
]
line = Morris.Line
element: 'graph'
data: [
{x: d[0].getTime(), y: 2},
{x: d[1].getTime(), y: 1},
{x: d[2].getTime(), y: 4},
{x: d[3].getTime(), y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.map((row) -> row.label).should.deep.equal d.map((t) -> t.toString())
it 'should use a user-supplied formatter for labels', ->
line = Morris.Line
element: 'graph'
data: [
{x: new Date(2012, 0, 1).getTime(), y: 2},
{x: new Date(2012, 0, 2).getTime(), y: 1},
{x: new Date(2012, 0, 3).getTime(), y: 4},
{x: new Date(2012, 0, 4).getTime(), y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
dateFormat: (ts) ->
date = new Date(ts)
"#{date.getFullYear()}-#{date.getMonth()+1}-#{date.getDate()}"
line.data.map((row) -> row.label).should.deep.equal ['2012-1-1', '2012-1-2', '2012-1-3', '2012-1-4']
it 'should parse y-values in strings', ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.ymin.should == 12
line.ymax.should == 16
line.data.map((row) -> row.y).should.deep.equal [[13.5], [12], [16], [14]]
it 'should clear the chart when empty data is supplied', ->
line = Morris.Line
element: 'graph',
data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.length.should.equal 4
line.setData([])
line.data.length.should.equal 0
line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
line.data.length.should.equal 4
it 'should be able to add data if the chart is initialised with empty data', ->
line = Morris.Line
element: 'graph',
data: []
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.length.should.equal 0
line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
line.data.length.should.equal 4
it 'should automatically choose significant numbers for y-labels', ->
line = Morris.Line
element: 'graph',
data: [{x: 1, y: 0}, {x: 2, y: 3600}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.grid.should == [0, 1000, 2000, 3000, 4000]

View File

@@ -0,0 +1,15 @@
describe 'Morris.Grid#yLabelFormat', ->
it 'should use custom formatter for y labels', ->
formatter = (label) ->
flabel = parseFloat(label) / 1000
"#{flabel.toFixed(1)}k"
line = Morris.Line
element: 'graph'
data: [{x: 1, y: 1500}, {x: 2, y: 2500}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
preUnits: "$"
yLabelFormat: formatter
line.yLabelFormat(1500).should.equal "1.5k"

View File

@@ -0,0 +1,64 @@
describe "Morris.Hover", ->
describe "with dummy content", ->
beforeEach ->
parent = $('<div style="width:200px;height:180px"></div>')
.appendTo($('#test'))
@hover = new Morris.Hover(parent: parent)
@element = $('#test .morris-hover')
it "should initialise a hidden, empty popup", ->
@element.should.exist
@element.should.be.hidden
@element.should.be.empty
describe "#show", ->
it "should show the popup", ->
@hover.show()
@element.should.be.visible
describe "#hide", ->
it "should hide the popup", ->
@hover.show()
@hover.hide()
@element.should.be.hidden
describe "#html", ->
it "should replace the contents of the element", ->
@hover.html('<div>Foobarbaz</div>')
@element.should.have.html('<div>Foobarbaz</div>')
describe "#moveTo", ->
beforeEach ->
@hover.html('<div style="width:84px;height:84px"></div>')
it "should place the popup directly above the given point", ->
@hover.moveTo(100, 150)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
it "should place the popup below the given point if it does not fit above", ->
@hover.moveTo(100, 50)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '60px')
it "should center the popup vertically if it will not fit above or below", ->
@hover.moveTo(100, 100)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
it "should center the popup vertically if no y value is supplied", ->
@hover.moveTo(100)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
describe "#update", ->
it "should update content, show and reposition the popup", ->
hover = new Morris.Hover(parent: $('#test'))
html = "<div style='width:84px;height:84px'>Hello, Everyone!</div>"
hover.update(html, 150, 200)
el = $('#test .morris-hover')
el.should.have.css('left', '100px')
el.should.have.css('top', '90px')
el.should.have.text('Hello, Everyone!')

View File

@@ -0,0 +1,172 @@
describe '#labelSeries', ->
it 'should generate decade intervals', ->
Morris.labelSeries(
new Date(1952, 0, 1).getTime(),
new Date(2012, 0, 1).getTime(),
1000
).should.deep.equal([
["1960", new Date(1960, 0, 1).getTime()],
["1970", new Date(1970, 0, 1).getTime()],
["1980", new Date(1980, 0, 1).getTime()],
["1990", new Date(1990, 0, 1).getTime()],
["2000", new Date(2000, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()]
])
Morris.labelSeries(
new Date(1952, 3, 1).getTime(),
new Date(2012, 3, 1).getTime(),
1000
).should.deep.equal([
["1960", new Date(1960, 0, 1).getTime()],
["1970", new Date(1970, 0, 1).getTime()],
["1980", new Date(1980, 0, 1).getTime()],
["1990", new Date(1990, 0, 1).getTime()],
["2000", new Date(2000, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()]
])
it 'should generate year intervals', ->
Morris.labelSeries(
new Date(2007, 0, 1).getTime(),
new Date(2012, 0, 1).getTime(),
1000
).should.deep.equal([
["2007", new Date(2007, 0, 1).getTime()],
["2008", new Date(2008, 0, 1).getTime()],
["2009", new Date(2009, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()],
["2011", new Date(2011, 0, 1).getTime()],
["2012", new Date(2012, 0, 1).getTime()]
])
Morris.labelSeries(
new Date(2007, 3, 1).getTime(),
new Date(2012, 3, 1).getTime(),
1000
).should.deep.equal([
["2008", new Date(2008, 0, 1).getTime()],
["2009", new Date(2009, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()],
["2011", new Date(2011, 0, 1).getTime()],
["2012", new Date(2012, 0, 1).getTime()]
])
it 'should generate month intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 5, 1).getTime(),
1000
).should.deep.equal([
["2012-01", new Date(2012, 0, 1).getTime()],
["2012-02", new Date(2012, 1, 1).getTime()],
["2012-03", new Date(2012, 2, 1).getTime()],
["2012-04", new Date(2012, 3, 1).getTime()],
["2012-05", new Date(2012, 4, 1).getTime()],
["2012-06", new Date(2012, 5, 1).getTime()]
])
it 'should generate day intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 0, 6).getTime(),
1000
).should.deep.equal([
["2012-01-01", new Date(2012, 0, 1).getTime()],
["2012-01-02", new Date(2012, 0, 2).getTime()],
["2012-01-03", new Date(2012, 0, 3).getTime()],
["2012-01-04", new Date(2012, 0, 4).getTime()],
["2012-01-05", new Date(2012, 0, 5).getTime()],
["2012-01-06", new Date(2012, 0, 6).getTime()]
])
it 'should generate hour intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0).getTime(),
new Date(2012, 0, 1, 5).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0).getTime()],
["01:00", new Date(2012, 0, 1, 1).getTime()],
["02:00", new Date(2012, 0, 1, 2).getTime()],
["03:00", new Date(2012, 0, 1, 3).getTime()],
["04:00", new Date(2012, 0, 1, 4).getTime()],
["05:00", new Date(2012, 0, 1, 5).getTime()]
])
it 'should generate half-hour intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0, 0).getTime(),
new Date(2012, 0, 1, 2, 30).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
["01:30", new Date(2012, 0, 1, 1, 30).getTime()],
["02:00", new Date(2012, 0, 1, 2, 0).getTime()],
["02:30", new Date(2012, 0, 1, 2, 30).getTime()]
])
Morris.labelSeries(
new Date(2012, 4, 12, 0, 0).getTime(),
new Date(2012, 4, 12, 2, 30).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
["01:30", new Date(2012, 4, 12, 1, 30).getTime()],
["02:00", new Date(2012, 4, 12, 2, 0).getTime()],
["02:30", new Date(2012, 4, 12, 2, 30).getTime()]
])
it 'should generate fifteen-minute intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0, 0).getTime(),
new Date(2012, 0, 1, 1, 15).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
["00:15", new Date(2012, 0, 1, 0, 15).getTime()],
["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
["00:45", new Date(2012, 0, 1, 0, 45).getTime()],
["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
["01:15", new Date(2012, 0, 1, 1, 15).getTime()]
])
Morris.labelSeries(
new Date(2012, 4, 12, 0, 0).getTime(),
new Date(2012, 4, 12, 1, 15).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
["00:15", new Date(2012, 4, 12, 0, 15).getTime()],
["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
["00:45", new Date(2012, 4, 12, 0, 45).getTime()],
["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
["01:15", new Date(2012, 4, 12, 1, 15).getTime()]
])
it 'should override automatic intervals', ->
Morris.labelSeries(
new Date(2011, 11, 12).getTime(),
new Date(2012, 0, 12).getTime(),
1000,
"year"
).should.deep.equal([
["2012", new Date(2012, 0, 1).getTime()]
])
it 'should apply custom formatters', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 0, 6).getTime(),
1000,
"day",
(d) -> "#{d.getMonth()+1}/#{d.getDate()}/#{d.getFullYear()}"
).should.deep.equal([
["1/1/2012", new Date(2012, 0, 1).getTime()],
["1/2/2012", new Date(2012, 0, 2).getTime()],
["1/3/2012", new Date(2012, 0, 3).getTime()],
["1/4/2012", new Date(2012, 0, 4).getTime()],
["1/5/2012", new Date(2012, 0, 5).getTime()],
["1/6/2012", new Date(2012, 0, 6).getTime()]
])

View File

@@ -0,0 +1,207 @@
describe 'Morris.Line', ->
it 'should raise an error when the placeholder element is not found', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
fn = ->
Morris.Line(
element: "thisplacedoesnotexist"
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
)
fn.should.throw(/Graph container element not found/)
it 'should make point styles customizable', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
red = '#ff0000'
blue = '#0000ff'
chart = Morris.Line
element: 'graph'
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
pointStrokeColors: [red, blue]
pointWidths: [1, 2]
pointFillColors: [null, red]
chart.strokeWidthForSeries(0).should.equal 1
chart.strokeForSeries(0).should.equal red
chart.strokeWidthForSeries(1).should.equal 2
chart.strokeForSeries(1).should.equal blue
chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line')
chart.colorFor(chart.data[1], 1, 'point').should.equal red
describe 'generating column labels', ->
it 'should use user-supplied x value strings by default', ->
chart = Morris.Line
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
chart.data.map((x) -> x.label).should == ['2012 Q1', '2012 Q2']
it 'should use a default format for timestamp x-values', ->
d1 = new Date(2012, 0, 1)
d2 = new Date(2012, 0, 2)
chart = Morris.Line
element: 'graph'
data: [{x: d1.getTime(), y: 1}, {x: d2.getTime(), y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
chart.data.map((x) -> x.label).should == [d2.toString(), d1.toString()]
it 'should use user-defined formatters', ->
d = new Date(2012, 0, 1)
chart = Morris.Line
element: 'graph'
data: [{x: d.getTime(), y: 1}, {x: '2012-01-02', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
dateFormat: (d) ->
x = new Date(d)
"#{x.getYear()}/#{x.getMonth()+1}/#{x.getDay()}"
chart.data.map((x) -> x.label).should == ['2012/1/1', '2012/1/2']
describe 'rendering lines', ->
beforeEach ->
@defaults =
element: 'graph'
data: [{x:0, y:1, z:0}, {x:1, y:0, z:1}, {x:2, y:1, z:0}, {x:3, y:0, z:1}, {x:4, y:1, z:0}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['y', 'z']
lineColors: ['#abcdef', '#fedcba']
smooth: true
continuousLine: false
shouldHavePath = (regex, color = '#abcdef') ->
# Matches an SVG path element within the rendered chart.
#
# Sneakily uses line colors to differentiate between paths within
# the chart.
$('#graph').find("path[stroke='#{color}']").attr('d').should.match regex
it 'should generate smooth lines when options.smooth is true', ->
Morris.Line @defaults
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/
it 'should generate jagged lines when options.smooth is false', ->
Morris.Line $.extend(@defaults, smooth: false)
shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/
it 'should generate smooth/jagged lines according to the value for each series when options.smooth is an array', ->
Morris.Line $.extend(@defaults, smooth: ['y'])
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/, '#abcdef'
shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/, '#fedcba'
it 'should ignore undefined values', ->
@defaults.data[2].y = undefined
Morris.Line @defaults
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){3}/
it 'should ignore null values when options.continuousLine is true', ->
@defaults.data[2].y = null
Morris.Line $.extend(@defaults, continuousLine: true)
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){3}/
it 'should break the line at null values when options.continuousLine is false', ->
@defaults.data[2].y = null
Morris.Line @defaults
shouldHavePath /(M[\d\.]+,[\d\.]+C[\d\.]+(,[\d\.]+){5}){2}/
describe '#createPath', ->
it 'should generate a smooth line', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M0,10C2.5,7.5,7.5,0,10,0C12.5,0,17.5,7.5,20,10'
it 'should generate a jagged line', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, false, 20)
path.should.equal 'M0,10L10,0L20,10'
it 'should prevent paths from descending below the bottom of the chart', ->
testData = [{x: 0, y: 20}, {x: 10, y: 30}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, true, 30)
path.should.equal 'M0,20C2.5,22.5,7.5,30,10,30C12.5,28.75,17.5,15,20,10'
it 'should break the line at null values', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: null}, {x: 30, y: 10}, {x: 40, y: 0}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M0,10C2.5,7.5,7.5,2.5,10,0M30,10C32.5,7.5,37.5,2.5,40,0'
it 'should ignore leading and trailing null values', ->
testData = [{x: 0, y: null}, {x: 10, y: 10}, {x: 20, y: 0}, {x: 30, y: 10}, {x: 40, y: null}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M10,10C12.5,7.5,17.5,0,20,0C22.5,0,27.5,7.5,30,10'
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
lineColors: [ '#0b62a4', '#7a92a3']
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
it 'should contain a path that represents the line', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain a circle for each data point', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle").size().should.equal 2
it 'should contain 5 grid lines', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
it 'should contain 9 text elements', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text").size().should.equal 9
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
lineColors: [ '#0b62a4', '#7a92a3']
lineWidth: 3
pointWidths: [5]
pointStrokeColors: ['#ffffff']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
it 'should have circles with configured fill color', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[fill='#0b62a4']").size().should.equal 2
it 'should have circles with configured stroke width', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[stroke-width='5']").size().should.equal 2
it 'should have circles with configured stroke color', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[stroke='#ffffff']").size().should.equal 2
it 'should have line with configured line width', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke-width='3']").size().should.equal 1
it 'should have text with configured font size', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text[font-size='12px']").size().should.equal 9
it 'should have text with configured font size', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text[fill='#888888']").size().should.equal 9

View File

@@ -0,0 +1,17 @@
describe '#pad', ->
it 'should pad numbers', ->
Morris.pad2(0).should.equal("00")
Morris.pad2(1).should.equal("01")
Morris.pad2(2).should.equal("02")
Morris.pad2(3).should.equal("03")
Morris.pad2(4).should.equal("04")
Morris.pad2(5).should.equal("05")
Morris.pad2(6).should.equal("06")
Morris.pad2(7).should.equal("07")
Morris.pad2(8).should.equal("08")
Morris.pad2(9).should.equal("09")
Morris.pad2(10).should.equal("10")
Morris.pad2(12).should.equal("12")
Morris.pad2(34).should.equal("34")
Morris.pad2(123).should.equal("123")

View File

@@ -0,0 +1,35 @@
describe '#parseTime', ->
it 'should parse years', ->
Morris.parseDate('2012').should.equal(new Date(2012, 0, 1).getTime())
it 'should parse quarters', ->
Morris.parseDate('2012 Q1').should.equal(new Date(2012, 2, 1).getTime())
it 'should parse months', ->
Morris.parseDate('2012-09').should.equal(new Date(2012, 8, 1).getTime())
Morris.parseDate('2012-10').should.equal(new Date(2012, 9, 1).getTime())
it 'should parse dates', ->
Morris.parseDate('2012-09-15').should.equal(new Date(2012, 8, 15).getTime())
Morris.parseDate('2012-10-15').should.equal(new Date(2012, 9, 15).getTime())
it 'should parse times', ->
Morris.parseDate("2012-10-15 12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
Morris.parseDate("2012-10-15T12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
Morris.parseDate("2012-10-15 12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
Morris.parseDate("2012-10-15T12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
it 'should parse times with timezones', ->
Morris.parseDate("2012-10-15T12:34+0100").should.equal(Date.UTC(2012, 9, 15, 11, 34))
Morris.parseDate("2012-10-15T12:34+02:00").should.equal(Date.UTC(2012, 9, 15, 10, 34))
Morris.parseDate("2012-10-15T12:34-0100").should.equal(Date.UTC(2012, 9, 15, 13, 34))
Morris.parseDate("2012-10-15T12:34-02:00").should.equal(Date.UTC(2012, 9, 15, 14, 34))
Morris.parseDate("2012-10-15T12:34:55Z").should.equal(Date.UTC(2012, 9, 15, 12, 34, 55))
Morris.parseDate("2012-10-15T12:34:55+0600").should.equal(Date.UTC(2012, 9, 15, 6, 34, 55))
Morris.parseDate("2012-10-15T12:34:55+04:00").should.equal(Date.UTC(2012, 9, 15, 8, 34, 55))
Morris.parseDate("2012-10-15T12:34:55-0600").should.equal(Date.UTC(2012, 9, 15, 18, 34, 55))
it 'should pass-through timestamps', ->
Morris.parseDate(new Date(2012, 9, 15, 12, 34, 55, 123).getTime())
.should.equal(new Date(2012, 9, 15, 12, 34, 55, 123).getTime())

View File

@@ -0,0 +1,35 @@
<!doctype html>
<head>
<meta charset="utf-8">
<title>morris.js tests</title>
<link rel="stylesheet" href="vendor/mocha-1.6.0.css" type="text/css" media="screen" />
<link rel="stylesheet" href="../morris.css" type="text/css" media="screen" />
<script src="vendor/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="vendor/raphael-2.1.0.min.js"></script>
</head>
<body>
<div id="mocha"></div>
<script type="text/javascript" src="vendor/mocha-1.6.0.js"></script>
<script>
mocha.setup('bdd');
</script>
<script type="text/javascript" src="vendor/chai-1.3.0.js"></script>
<script type="text/javascript" src="vendor/chai-jquery-1.1.0.js"></script>
<script type="text/javascript" src="vendor/sinon-1.5.0.js"></script>
<script type="text/javascript" src="vendor/sinon-chai-2.1.2.js"></script>
<script>
should = chai.should();
</script>
<script type="text/javascript" src="../morris.js"></script>
<script type="text/javascript" src="../build/spec.js"></script>
<div id="test" style="width: 400px; height: 200px;"></div>
<script>
if (navigator.userAgent.indexOf('PhantomJS') < 0) {
mocha.run();
}
</script>
</body>
</html>

View File

@@ -0,0 +1,6 @@
beforeEach ->
placeholder = $('<div id="graph" style="width: 600px; height: 400px"></div>')
$('#test').append(placeholder)
afterEach ->
$('#test').empty()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
(function (chaiJquery) {
// Module systems magic dance.
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
// NodeJS
module.exports = chaiJquery;
} else if (typeof define === "function" && define.amd) {
// AMD
define(function () {
return chaiJquery;
});
} else {
// Other environment (usually <script> tag): plug in to global chai instance directly.
chai.use(chaiJquery);
}
}(function (chai, utils) {
var inspect = utils.inspect,
flag = utils.flag;
jQuery.fn.inspect = function (depth) {
var el = jQuery('<div />').append(this.clone());
if (depth) {
var children = el.children();
while (depth-- > 0)
children = children.children();
children.html('...');
}
return el.html();
};
var props = {attr: 'attribute', css: 'CSS property'};
for (var prop in props) {
(function (prop, description) {
chai.Assertion.addMethod(prop, function (name, val) {
var actual = flag(this, 'object')[prop](name);
if (!flag(this, 'negate') || undefined === val) {
this.assert(
undefined !== actual
, 'expected #{this} to have a #{exp} ' + description
, 'expected #{this} not to have a #{exp} ' + description
, name
);
}
if (undefined !== val) {
this.assert(
val === actual
, 'expected #{this} to have a ' + inspect(name) + ' ' + description + ' with the value #{exp}, but the value was #{act}'
, 'expected #{this} not to have a ' + inspect(name) + ' ' + description + ' with the value #{act}'
, val
, actual
);
}
flag(this, 'object', actual);
});
})(prop, props[prop]);
}
chai.Assertion.addMethod('data', function (name, val) {
// Work around a chai bug (https://github.com/logicalparadox/chai/issues/16)
if (flag(this, 'negate') && undefined !== val && undefined === flag(this, 'object').data(name)) {
return;
}
var assertion = new chai.Assertion(flag(this, 'object').data());
if (flag(this, 'negate'))
assertion = assertion.not;
return assertion.property(name, val);
});
chai.Assertion.addMethod('class', function (className) {
this.assert(
flag(this, 'object').hasClass(className)
, 'expected #{this} to have class #{exp}'
, 'expected #{this} not to have class #{exp}'
, className
);
});
chai.Assertion.addMethod('id', function (id) {
this.assert(
flag(this, 'object').attr('id') === id
, 'expected #{this} to have id #{exp}'
, 'expected #{this} not to have id #{exp}'
, id
);
});
chai.Assertion.addMethod('html', function (html) {
this.assert(
flag(this, 'object').html() === html
, 'expected #{this} to have HTML #{exp}'
, 'expected #{this} not to have HTML #{exp}'
, html
);
});
chai.Assertion.addMethod('text', function (text) {
this.assert(
flag(this, 'object').text() === text
, 'expected #{this} to have text #{exp}'
, 'expected #{this} not to have text #{exp}'
, text
);
});
chai.Assertion.addMethod('value', function (value) {
this.assert(
flag(this, 'object').val() === value
, 'expected #{this} to have value #{exp}'
, 'expected #{this} not to have value #{exp}'
, value
);
});
jQuery.each(['visible', 'hidden', 'selected', 'checked', 'disabled'], function (i, attr) {
chai.Assertion.addProperty(attr, function () {
this.assert(
flag(this, 'object').is(':' + attr)
, 'expected #{this} to be ' + attr
, 'expected #{this} not to be ' + attr);
});
});
chai.Assertion.overwriteProperty('exist', function (_super) {
return function () {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
this.assert(
obj.length > 0
, 'expected ' + inspect(obj.selector) + ' to exist'
, 'expected ' + inspect(obj.selector) + ' not to exist');
} else {
_super.apply(this, arguments);
}
};
});
chai.Assertion.overwriteProperty('empty', function (_super) {
return function () {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
this.assert(
obj.is(':empty')
, 'expected #{this} to be empty'
, 'expected #{this} not to be empty');
} else {
_super.apply(this, arguments);
}
};
});
chai.Assertion.overwriteProperty('be', function (_super) {
return function () {
var be = function (selector) {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
this.assert(
obj.is(selector)
, 'expected #{this} to be #{exp}'
, 'expected #{this} not to be #{exp}'
, selector
);
} else {
_super.apply(this, arguments);
}
};
be.__proto__ = this;
return be;
}
});
chai.Assertion.overwriteMethod('match', function (_super) {
return function (selector) {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
this.assert(
obj.is(selector)
, 'expected #{this} to match #{exp}'
, 'expected #{this} not to match #{exp}'
, selector
);
} else {
_super.apply(this, arguments);
}
}
});
chai.Assertion.overwriteProperty('contain', function (_super) {
return function () {
_super.call(this);
var contain = function (text) {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
this.assert(
obj.is(':contains(\'' + text + '\')')
, 'expected #{this} to contain #{exp}'
, 'expected #{this} not to contain #{exp}'
, text
);
} else {
Function.prototype.apply.call(_super.call(this), this, arguments);
}
};
contain.__proto__ = this;
return contain;
}
});
chai.Assertion.overwriteProperty('have', function (_super) {
return function () {
var obj = flag(this, 'object');
if (obj instanceof jQuery) {
var have = function (selector) {
this.assert(
// Using find() rather than has() to work around a jQuery bug:
// http://bugs.jquery.com/ticket/11706
obj.find(selector).length > 0
, 'expected #{this} to have #{exp}'
, 'expected #{this} not to have #{exp}'
, selector
);
};
have.__proto__ = this;
return have;
} else {
_super.call(this);
}
}
});
}));

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,203 @@
@charset "UTF-8";
body {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 60px 50px;
}
#mocha ul, #mocha li {
margin: 0;
padding: 0;
}
#mocha ul {
list-style: none;
}
#mocha h1, #mocha h2 {
margin: 0;
}
#mocha h1 {
margin-top: 15px;
font-size: 1em;
font-weight: 200;
}
#mocha h1 a {
text-decoration: none;
color: inherit;
}
#mocha h1 a:hover {
text-decoration: underline;
}
#mocha .suite .suite h1 {
margin-top: 0;
font-size: .8em;
}
.hidden {
display: none;
}
#mocha h2 {
font-size: 12px;
font-weight: normal;
cursor: pointer;
}
#mocha .suite {
margin-left: 15px;
}
#mocha .test {
margin-left: 15px;
}
#mocha .test:hover h2::after {
position: relative;
top: 0;
right: -10px;
content: '(view source)';
font-size: 12px;
font-family: arial;
color: #888;
}
#mocha .test.pending:hover h2::after {
content: '(pending)';
font-family: arial;
}
#mocha .test.pass.medium .duration {
background: #C09853;
}
#mocha .test.pass.slow .duration {
background: #B94A48;
}
#mocha .test.pass::before {
content: '✓';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #00d6b2;
}
#mocha .test.pass .duration {
font-size: 9px;
margin-left: 5px;
padding: 2px 5px;
color: white;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#mocha .test.pass.fast .duration {
display: none;
}
#mocha .test.pending {
color: #0b97c4;
}
#mocha .test.pending::before {
content: '◦';
color: #0b97c4;
}
#mocha .test.fail {
color: #c00;
}
#mocha .test.fail pre {
color: black;
}
#mocha .test.fail::before {
content: '✖';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #c00;
}
#mocha .test pre.error {
color: #c00;
}
#mocha .test pre {
display: inline-block;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
border-bottom-color: #ddd;
-webkit-border-radius: 3px;
-webkit-box-shadow: 0 1px 3px #eee;
}
#report.pass .test.fail {
display: none;
}
#report.fail .test.pass {
display: none;
}
#error {
color: #c00;
font-size: 1.5 em;
font-weight: 100;
letter-spacing: 1px;
}
#stats {
position: fixed;
top: 15px;
right: 10px;
font-size: 12px;
margin: 0;
color: #888;
}
#stats .progress {
float: right;
padding-top: 0;
}
#stats em {
color: black;
}
#stats a {
text-decoration: none;
color: inherit;
}
#stats a:hover {
border-bottom: 1px solid #eee;
}
#stats li {
display: inline-block;
margin: 0 5px;
list-style: none;
padding-top: 11px;
}
code .comment { color: #ddd }
code .init { color: #2F6FAD }
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
(function (sinonChai) {
"use strict";
// Module systems magic dance.
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
// NodeJS
module.exports = sinonChai;
} else if (typeof define === "function" && define.amd) {
// AMD
define(function () {
return sinonChai;
});
} else {
// Other environment (usually <script> tag): plug in to global chai instance directly.
chai.use(sinonChai);
}
}(function sinonChai(chai, utils) {
"use strict";
var slice = Array.prototype.slice;
function isSpy(putativeSpy) {
return typeof putativeSpy === "function" &&
typeof putativeSpy.getCall === "function" &&
typeof putativeSpy.calledWithExactly === "function";
}
function isCall(putativeCall) {
return putativeCall && isSpy(putativeCall.proxy);
}
function assertCanWorkWith(assertion) {
if (!isSpy(assertion._obj) && !isCall(assertion._obj)) {
throw new TypeError(utils.inspect(assertion._obj) + " is not a spy or a call to a spy!");
}
}
function getMessages(spy, action, nonNegatedSuffix, always, args) {
var verbPhrase = always ? "always have " : "have ";
nonNegatedSuffix = nonNegatedSuffix || "";
spy = spy.proxy || spy;
function printfArray(array) {
return spy.printf.apply(spy, array);
}
return {
affirmative: printfArray(["expected %n to " + verbPhrase + action + nonNegatedSuffix].concat(args)),
negative: printfArray(["expected %n to not " + verbPhrase + action].concat(args))
};
}
function sinonProperty(name, action, nonNegatedSuffix) {
utils.addProperty(chai.Assertion.prototype, name, function () {
assertCanWorkWith(this);
var messages = getMessages(this._obj, action, nonNegatedSuffix, false);
this.assert(this._obj[name], messages.affirmative, messages.negative);
});
}
function exceptionalSinonMethod(chaiName, sinonName, action, nonNegatedSuffix) {
utils.addMethod(chai.Assertion.prototype, chaiName, function () {
assertCanWorkWith(this);
var alwaysSinonMethod = "always" + sinonName[0].toUpperCase() + sinonName.substring(1);
var shouldBeAlways = utils.flag(this, "always") && typeof this._obj[alwaysSinonMethod] === "function";
var sinonMethod = shouldBeAlways ? alwaysSinonMethod : sinonName;
var messages = getMessages(this._obj, action, nonNegatedSuffix, shouldBeAlways, slice.call(arguments));
this.assert(this._obj[sinonMethod].apply(this._obj, arguments), messages.affirmative, messages.negative);
});
}
function sinonMethod(name, action, nonNegatedSuffix) {
exceptionalSinonMethod(name, name, action, nonNegatedSuffix);
}
utils.addProperty(chai.Assertion.prototype, "always", function () {
utils.flag(this, "always", true);
});
sinonProperty("called", "been called", " at least once, but it was never called");
sinonProperty("calledOnce", "been called exactly once", ", but it was called %c%C");
sinonProperty("calledTwice", "been called exactly twice", ", but it was called %c%C");
sinonProperty("calledThrice", "been called exactly thrice", ", but it was called %c%C");
sinonMethod("calledBefore", "been called before %1");
sinonMethod("calledAfter", "been called after %1");
sinonMethod("calledOn", "been called with %1 as this", ", but it was called with %t instead");
sinonMethod("calledWith", "been called with arguments %*", "%C");
sinonMethod("calledWithExactly", "been called with exact arguments %*", "%C");
sinonMethod("returned", "returned %1");
exceptionalSinonMethod("thrown", "threw", "thrown %1");
}));