@@ -5,7 +5,7 @@ var createGraphDiv = require('../assets/create_graph_div');
5
5
var destroyGraphDiv = require ( '../assets/destroy_graph_div' ) ;
6
6
7
7
// Boilerplate taken from axes_test.js
8
- describe ( 'tickmode proportional' , function ( ) {
8
+ describe ( 'When generating axes w/ `tickmode`:" proportional", ' , function ( ) {
9
9
var gd ;
10
10
11
11
beforeEach ( function ( ) {
@@ -16,7 +16,7 @@ describe('tickmode proportional', function() {
16
16
17
17
// These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
18
18
// Passed as tickLen argument to specify major or minor tick config
19
- const MAJOR = 202 , MINOR = 101 ;
19
+ const MAJOR = 10 , MINOR = 5 ;
20
20
function generateTickConfig ( tickLen ) {
21
21
// Intentionally configure to produce a single `(x|y)tick` class per tick
22
22
// labels and tick marks each produce one, so one or the other
@@ -33,119 +33,162 @@ describe('tickmode proportional', function() {
33
33
standardConfig [ 'tickvals' ] = tickVals ;
34
34
return standardConfig ;
35
35
}
36
-
37
- function binaryToString ( bin ) {
38
- if ( bin == 0b1 ) return "xMajor" ;
39
- if ( bin == 0b10 ) return "xMinor" ;
40
- if ( bin == 0b100 ) return "yMajor" ;
41
- if ( bin == 0b1000 ) return "yMinor" ;
36
+
37
+ // See comment below for explanation of parameterization
38
+ const XMAJOR = 0b0001 ;
39
+ const XMINOR = 0b0010 ;
40
+ const YMAJOR = 0b0100 ;
41
+ const YMINOR = 0b1000 ;
42
+ // Converts binary to list of tick types indicated by binary
43
+ function binaryToTickType ( bin ) {
44
+ str = [ ] ;
45
+ if ( bin & XMAJOR ) str . push ( "xMajor" ) ;
46
+ if ( bin & XMINOR ) str . push ( "xMinor" ) ;
47
+ if ( bin & YMAJOR ) str . push ( "yMajor" ) ;
48
+ if ( bin & YMINOR ) str . push ( "yMinor" ) ;
49
+ if ( str . length ) {
50
+ return str . join ( ', ' ) ;
51
+ }
52
+ return "" ;
42
53
}
54
+
43
55
// the var `tickConfig` represents every possible configuration. It is in an int 0-15.
44
56
// The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
45
- for ( let tickConfig = 1 ; tickConfig <= 15 ; tickConfig ++ ) {
46
- ( function ( tickConfig ) { // tickConfig needs to be a closure otherwise it won't get the parameterized value
47
- it ( 'maps proportional values to correct range values' , function ( done ) {
48
- var xMajor = tickConfig & 0b0001 ; // check if xMajor position is 1 (True)
49
- var xMinor = tickConfig & 0b0010 ;
50
- var yMajor = tickConfig & 0b0100 ;
51
- var yMinor = tickConfig & 0b1000 ;
52
- xMajorConfig = xMajor ? generateTickConfig ( MAJOR ) : { } ; // generate separate configs for each
53
- xMinorConfig = xMinor ? generateTickConfig ( MAJOR ) : { } ;
54
- yMajorConfig = yMajor ? generateTickConfig ( MINOR ) : { } ;
55
- yMinorConfig = yMinor ? generateTickConfig ( MINOR ) : { } ;
56
- var configInfo = ""
57
- configInfo += xMajor ? "\n" + `xMajor: ${ xMajorConfig [ 'tickvals' ] . length } non-unique vals` : "" ;
58
- configInfo += xMinor ? "\n" + `xMinor: ${ xMinorConfig [ 'tickvals' ] . length } non-unique vals` : "" ;
59
- configInfo += yMajor ? "\n" + `yMajor: ${ yMajorConfig [ 'tickvals' ] . length } non-unique vals` : "" ;
60
- configInfo += yMinor ? "\n" + `yMinor: ${ yMinorConfig [ 'tickvals' ] . length } non-unique vals` : "" ;
61
- Plotly . newPlot ( gd , {
62
- data : [ {
63
- x : [ 0 , 1 ] ,
64
- y : [ 0 , 1 ]
65
- } ] ,
66
- layout : {
67
- width : 400 ,
68
- height : 400 ,
69
- margin : { t : 40 , b : 40 , l : 40 , r : 40 , } ,
70
- xaxis : {
71
- range : [ 0 , 10 ] ,
72
- ...xMajorConfig , // explode config into this key
73
- minor : xMinorConfig , // set config to this key
74
- } ,
75
- yaxis : { // same as above
76
- autorange : true ,
77
- ...yMajorConfig ,
78
- minor : yMinorConfig ,
79
- } ,
80
- } } ) . then ( function ( ) {
81
- // This regex is for extracting geometric position of... should have used getBoundingClientRect()
82
- //
83
- // regex: `.source` converts to string, laid out this way to make for easier reading
84
- const funcName = "translate" + / \( / . source ; // literally simplest way to regex '('
85
- const integerPart = / \d + / . source ; // numbers left of decimal
86
- const fractionalPart = / (?: \. \d + ) ? / . source ; // decimal + numbers to right
87
- const floatNum = integerPart + fractionalPart ; // all together
88
- const any = / .+ / . source ;
89
- const close = / \) / . source ;
90
- const re = new RegExp ( funcName + '(' + floatNum + '),' + any + close ) ; // parens are capture not fn()
91
- for ( let runNumber = 0b1 ; runNumber <= 0b1000 ; runNumber <<= 0b1 ) { // Check all ticks on all axes ☺
92
- var elementName = "" ;
93
- var targetConfig ;
94
- var runInfo = "\n Checking: " + binaryToString ( runNumber ) ;
95
- if ( runNumber & xMajor ) { // ie. this run wants xMajor and xMajor was set in config above
96
- elementName = "xtick" ;
97
- targetConfig = xMajorConfig ;
98
- } else if ( runNumber & xMinor ) {
99
- elementName = "xtick" ;
100
- targetConfig = xMinorConfig ;
101
- } else if ( runNumber & yMajor ) {
102
- elementName = "ytick" ;
103
- targetConfig = yMajorConfig ;
104
- } else if ( runNumber & yMinor ) {
105
- elementName = "ytick" ;
106
- targetConfig = yMinorConfig ;
107
- } else continue ; // This test isn't doing that type of test
108
-
109
- var tickElements = document . getElementsByClassName ( elementName ) ;
110
- var tickValsUnique = [ ...new Set ( targetConfig [ 'tickvals' ] ) ] ;
111
- var expectedTickLen = String ( targetConfig [ 'ticklen' ] )
57
+ // We add a fourth to switch between linear and log
58
+ for ( let tickConfig = 1 ; tickConfig <= 0b1111 ; tickConfig ++ ) {
59
+ var graphTypes = [
60
+ { type :'linear' } ,
61
+ { type :'log' } ,
62
+ { type :'date' } ,
63
+ { type :'category' } ,
64
+ ] ;
65
+ for ( let graphTypeIndex = 0 ; graphTypeIndex < graphTypes . length ; graphTypeIndex ++ ) {
66
+ var xGraphType = graphTypes [ graphTypeIndex ] ; // Only with X for now
67
+ ( function ( tickConfig , xGraphType ) { // wrap in func or else it() can't see variable because of javascript closure scope
68
+ it ( 'fraction mapping to geometries for config ' + binaryToTickType ( tickConfig ) , function ( done ) {
69
+ // We will check all four tick sets, these will resolve to true or false:
70
+ var xMajor = tickConfig & XMAJOR ;
71
+ var xMinor = tickConfig & XMINOR ;
72
+ var yMajor = tickConfig & YMAJOR ;
73
+ var yMinor = tickConfig & YMINOR ;
74
+ ticksOff = { ticklen : 0 , showticklabels : false } ;
75
+ var xMajorConfig = xMajor ? generateTickConfig ( MAJOR ) : ticksOff ; // generate separate configs for each
76
+ var xMinorConfig = xMinor ? generateTickConfig ( MINOR ) : ticksOff ;
77
+ var yMajorConfig = yMajor ? generateTickConfig ( MAJOR ) : ticksOff ;
78
+ var yMinorConfig = yMinor ? generateTickConfig ( MINOR ) : ticksOff ;
79
+ var configInfo = ""
80
+ configInfo += xMajor ? "\n " + `xMajor: ${ [ ...new Set ( xMajorConfig [ 'tickvals' ] ) ] . length } unique vals` : "" ;
81
+ configInfo += xMinor ? "\n " + `xMinor: ${ [ ...new Set ( xMinorConfig [ 'tickvals' ] ) ] . length } unique vals` : "" ;
82
+ configInfo += yMajor ? "\n " + `yMajor: ${ [ ...new Set ( yMajorConfig [ 'tickvals' ] ) ] . length } unique vals` : "" ;
83
+ configInfo += yMinor ? "\n " + `yMinor: ${ [ ...new Set ( yMinorConfig [ 'tickvals' ] ) ] . length } unique vals` : "" ;
84
+ Plotly . newPlot ( gd , {
85
+ data : [ {
86
+ x : [ 0 , 1 ] ,
87
+ y : [ 0 , 1 ]
88
+ } ] ,
89
+ layout : {
90
+ width : 400 ,
91
+ height : 400 ,
92
+ margin : { t : 40 , b : 40 , l : 40 , r : 40 , } ,
93
+ ...xGraphType ,
94
+ xaxis : {
95
+ autorange : true ,
96
+ ...xMajorConfig , // explode config into this key
97
+ minor : xMinorConfig , // set config to this key
98
+ } ,
99
+ yaxis : { // same as above
100
+ autorange : true ,
101
+ ...yMajorConfig ,
102
+ minor : yMinorConfig ,
103
+ } ,
104
+ } } ) . then ( function ( ) {
105
+ // This regex is for extracting geometric position of... should have used getBoundingClientRect()
106
+ //
107
+ // regex: `.source` converts to string, laid out this way to make for easier reading
108
+ const funcName = "translate" + / \( / . source ; // literally simplest way to regex '('
109
+ const integerPart = / \d + / . source ; // numbers left of decimal
110
+ const fractionalPart = / (?: \. \d + ) ? / . source ; // decimal + numbers to right
111
+ const floatNum = integerPart + fractionalPart ; // all together
112
+ const any = / .+ / . source ;
113
+ const close = / \) / . source ;
114
+ const reX = new RegExp ( funcName + '(' + floatNum + '),' + any + close ) ; // parens are capture not fn()
115
+ const reY = new RegExp ( funcName + any + ',(' + floatNum + ')' + close ) ; // parens are capture not fn()
112
116
113
- if ( tickElements . length < 2 ) return ; // Can't test proportions with < 2 ticks (since no fixed reference)
114
-
115
- // Filter out major/minor and grab geometry
116
- transformVals = [ ] ; // "transform" ie the positional property
117
- for ( let i = 0 ; i < tickElements . length ; i ++ ) {
118
- if ( ! tickElements [ i ] . getAttribute ( "d" ) . endsWith ( expectedTickLen ) ) continue ;
119
- var translate = tickElements [ i ] . getAttribute ( "transform" ) ;
120
- transformVals . push ( Number ( translate . match ( re ) [ 1 ] ) ) ;
121
- }
122
-
123
- var debugInfo = "\n " + `tickElements: (${ tickElements . length } ) ${ tickElements } ` + "\n" +
124
- `tickVals/Unique: (${ targetConfig [ 'tickvals' ] . length } /${ tickValsUnique . length } ) {tickValsUnique}` ;
125
-
126
- expect ( transformVals . length ) . toBe ( tickValsUnique . length ,
127
- "test html vs tickvals failed" + runInfo + configInfo + debugInfo ) ;
117
+ for ( let runNumber = 0b1 ; runNumber <= 0b1000 ; runNumber <<= 0b1 ) { // Check all ticks on all axes ☺
118
+ var runInfo = "\n Checking: " + binaryToTickType ( runNumber ) ;
119
+ var elementName = "" ;
120
+ var targetConfig ;
121
+ var re ;
122
+ if ( runNumber & xMajor ) { // ie. (this run wants xMajor) & (xMajor was set in config above)
123
+ elementName = "xtick" ;
124
+ targetConfig = xMajorConfig ;
125
+ re = reX ;
126
+ } else if ( runNumber & xMinor ) {
127
+ elementName = "xtick" ;
128
+ targetConfig = xMinorConfig ;
129
+ re = reX ;
130
+ } else if ( runNumber & yMajor ) {
131
+ elementName = "ytick" ;
132
+ targetConfig = yMajorConfig ;
133
+ re = reY ;
134
+ } else if ( runNumber & yMinor ) {
135
+ elementName = "ytick" ;
136
+ targetConfig = yMinorConfig ;
137
+ re = reY ;
138
+ } else continue ; // This run would have been to check ticks that don't exist
139
+
140
+ var tickElements = document . getElementsByClassName ( elementName ) ;
141
+ var tickValsUnique = [ ...new Set ( targetConfig [ 'tickvals' ] ) ] ;
142
+ var expectedTickLen = String ( targetConfig [ 'ticklen' ] )
143
+
144
+
145
+ // Filter out major/minor and grab geometry
146
+ transformVals = [ ] ; // "transform" ie the positional property
147
+ for ( let i = 0 ; i < tickElements . length ; i ++ ) {
148
+ if ( ! tickElements [ i ] . getAttribute ( "d" ) . endsWith ( expectedTickLen ) ) continue ;
149
+ var translate = tickElements [ i ] . getAttribute ( "transform" ) ;
150
+ transformVals . push ( Number ( translate . match ( re ) [ 1 ] ) ) ;
151
+ }
152
+
153
+ var debugInfo = "\n " + `tickElements: (${ tickElements . length } ) ${ tickElements } ` + "\n " +
154
+ `tickVals/Unique: (${ targetConfig [ 'tickvals' ] . length } /${ tickValsUnique . length } ) {tickValsUnique}` ;
155
+
156
+ expect ( transformVals . length ) . toBe ( tickValsUnique . length ,
157
+ "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo ) ;
158
+ if ( transformVals . length < 2 ) return ; // Can't test proportions with < 2 ticks (since no fixed reference)
159
+
160
+
161
+ // To test geometries without using fixed point or data values
162
+ // We can check consistency of y = mx+b
163
+ // if x = 0 then y = b, but we may not have a 0 valued x
164
+ // m = (y1 - y2) / (x1 - x2)
165
+ // b = y1 - mx1
166
+ y = transformVals ;
167
+ x = tickValsUnique ;
168
+ var m , b ;
169
+ var b_index = x . indexOf ( 0 ) ;
170
+
171
+ m = ( y [ 0 ] - y [ 1 ] ) / ( x [ 0 ] - x [ 1 ] ) ;
172
+ b = ( b_index != - 1 ) ? b = y [ b_index ] : y [ 0 ] - m * x [ 0 ] ;
173
+
174
+ calculatedY = [ ] ;
175
+ for ( let i = 0 ; i < x . length ; i ++ ) calculatedY . push ( m * x [ i ] + b ) ;
176
+
128
177
129
-
130
- // To test geometries without using fixed point or data values
131
- // We can check consistency of y = mx+b
132
- // if x = 0 then y = b, but we may not have a 0 valued x
133
- // m = (y1 - y2) / (x1 - x2)
134
- // b = y1 - mx1
135
- y = transformVals ;
136
- x = tickValsUnique ;
137
- var m , b ;
138
- var b_index = x . indexOf ( 0 ) ;
139
-
140
- m = ( y [ 0 ] - y [ 1 ] ) / ( x [ 0 ] - x [ 1 ] ) ;
141
- b = ( b_index != - 1 ) ? b = y [ b_index ] : y [ 0 ] - m * x [ 0 ] ;
142
-
143
- for ( let i = 0 ; i < x . length ; i ++ ) {
144
- expect ( y [ i ] ) . toBeCloseTo ( m * x [ i ] + b , 1 , "y=mx+b test failed" + runInfo + configInfo + debugInfo ) // not sure about toBeCloseTo
178
+ /* **** Close this comment line to manually inspect output -->
179
+ yout = [];
180
+ ycalcout = [];
181
+ for (i = 0; i < Math.min(x.length, 10); i++) {
182
+ yout.push(Number.parseFloat(y[i]).toFixed(2));
183
+ ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
184
+ }
185
+ console.log(yout);
186
+ console.log(ycalcout);/* */
187
+ expect ( y ) . toBeCloseToArray ( calculatedY , `y=mx+b test failed comparing\n${ y } \n${ calculatedY } ` ) ;
145
188
}
146
- }
147
- } ) . then ( done , done . fail ) ;
148
- } ) ;
149
- } ) ( tickConfig ) ;
189
+ } ) . then ( done , done . fail ) ;
190
+ } ) ;
191
+ } ) ( tickConfig , xGraphType ) ;
192
+ }
150
193
}
151
194
} ) ;
0 commit comments