@@ -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 ( 'When generating axes w/ `tickmode`:"domain array" ,' , function ( ) {
8
+ describe ( 'Generating ticks with `tickmode`,' , function ( ) {
9
9
var gd ;
10
10
11
11
beforeEach ( function ( ) {
@@ -18,20 +18,25 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
18
18
// Passed as tickLen argument to specify a major or minor tick config
19
19
const MAJOR = 10 ;
20
20
const MINOR = 5 ;
21
- function generateTickConfig ( tickLen ) {
21
+ function generateTickConfig ( tickLen , tickmode , nticks ) {
22
+ if ( tickmode === undefined ) tickmode = 'domain array' ;
22
23
// Intentionally configure to produce a single `(x|y)tick` class per tick
23
24
// labels and tick marks each produce one, so one or the other
24
- standardConfig = { tickmode : 'domain array' , ticklen : tickLen , showticklabels : false } ;
25
+ standardConfig = { tickmode : tickmode , ticklen : tickLen , showticklabels : false } ;
25
26
26
27
// Tick values will be random:
27
- var n = Math . floor ( Math . random ( ) * 100 ) ;
28
- tickVals = [ ] ;
28
+ if ( tickmode === 'domain array' ) {
29
+ var n = Math . floor ( Math . random ( ) * 100 ) ;
30
+ tickVals = [ ] ;
29
31
30
- for ( let i = 0 ; i <= n ; i ++ ) {
31
- intermediate = ( Math . trunc ( Math . random ( ) * 150 ) - 25 ) / 100 ; // Number between -.25 and 1.25 w/ 2 decimals max
32
- tickVals . push ( Math . min ( Math . max ( intermediate , 0 ) , 1 ) ) ; // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
32
+ for ( let i = 0 ; i <= n ; i ++ ) {
33
+ intermediate = ( Math . trunc ( Math . random ( ) * 150 ) - 25 ) / 100 ; // Number between -.25 and 1.25 w/ 2 decimals max
34
+ tickVals . push ( Math . min ( Math . max ( intermediate , 0 ) , 1 ) ) ; // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
35
+ }
36
+ standardConfig [ 'tickvals' ] = tickVals ;
37
+ } else if ( tickmode === 'full domain' ) {
38
+ standardConfig [ 'nticks' ] = nticks ;
33
39
}
34
- standardConfig [ 'tickvals' ] = tickVals ;
35
40
return standardConfig ;
36
41
}
37
42
@@ -63,9 +68,9 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
63
68
{ type :'date' } ,
64
69
{ type :'category' } ,
65
70
] ;
66
- for ( let graphTypeIndex = 0 ; graphTypeIndex < graphTypes . length ; graphTypeIndex ++ ) {
71
+ for ( let graphTypeIndex = 0 ; graphTypeIndex < graphTypes . length ; graphTypeIndex ++ ) {
67
72
( 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 ) {
73
+ it ( '"domain array" and config ' + binaryToTickType ( tickConfig ) , function ( done ) {
69
74
var xMajor = tickConfig & XMAJOR ; // does this config include xmajor?
70
75
var xMinor = tickConfig & XMINOR ; // does this config include xminor?
71
76
var yMajor = tickConfig & YMAJOR ; // ... etc
@@ -188,6 +193,148 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
188
193
}
189
194
} ) . then ( done , done . fail ) ;
190
195
} ) ;
196
+ for ( let nticks_param = 0 ; nticks_param < 5 ; nticks_param ++ ) {
197
+ ( function ( nticks_param ) {
198
+ it ( '"full domain" and config ' + binaryToTickType ( tickConfig ) , function ( done ) {
199
+ var xMajor = tickConfig & XMAJOR ; // does this config include xmajor?
200
+ var xMinor = tickConfig & XMINOR ; // does this config include xminor?
201
+ var yMajor = tickConfig & YMAJOR ; // ... etc
202
+ var yMinor = tickConfig & YMINOR ;
203
+ ticksOff = { ticklen : 0 , showticklabels : false } ;
204
+ var xMajorConfig = xMajor ? generateTickConfig ( MAJOR , 'full domain' , nticks_param ) : ticksOff ; // generate configs
205
+ var xMinorConfig = xMinor ? generateTickConfig ( MINOR , 'full domain' , nticks_param ) : ticksOff ;
206
+ var yMajorConfig = yMajor ? generateTickConfig ( MAJOR , 'full domain' , nticks_param ) : ticksOff ;
207
+ var yMinorConfig = yMinor ? generateTickConfig ( MINOR , 'full domain' , nticks_param ) : ticksOff ;
208
+ var configInfo = "" // for debugging
209
+ configInfo += xMajor ? "\n " + `xMajor nticks: ${ xMajorConfig [ 'nticks' ] } ` : "" ;
210
+ configInfo += xMinor ? "\n " + `xMinor nticks: ${ xMinorConfig [ 'nticks' ] } ` : "" ;
211
+ configInfo += yMajor ? "\n " + `yMajor nticks: ${ yMajorConfig [ 'nticks' ] } ` : "" ;
212
+ configInfo += yMinor ? "\n " + `yMinor nticks: ${ yMinorConfig [ 'nticks' ] } ` : "" ;
213
+
214
+ // stolen from axes_test.js
215
+ Plotly . newPlot ( gd , {
216
+ data : [ {
217
+ x : [ 0 , 1 ] ,
218
+ y : [ 0 , 1 ]
219
+ } ] ,
220
+ layout : {
221
+ width : 400 ,
222
+ height : 400 ,
223
+ margin : { t : 40 , b : 40 , l : 40 , r : 40 , } ,
224
+ ...xGraphType ,
225
+ xaxis : {
226
+ autorange : true ,
227
+ ...xMajorConfig , // explode config into this key
228
+ minor : xMinorConfig , // set config to this key
229
+ } ,
230
+ yaxis : { // same as above
231
+ autorange : true ,
232
+ ...yMajorConfig ,
233
+ minor : yMinorConfig ,
234
+ } ,
235
+ } } ) . then ( function ( ) {
236
+ // This regex is for extracting geometric position of a tick
237
+ // regex: `.source` converts to string, laid out this way to make for easier reading
238
+ const funcName = "translate" + / \( / . source ; // literally simplest way to regex '('
239
+ const integerPart = / \d + / . source ; // numbers left of decimal
240
+ const fractionalPart = / (?: \. \d + ) ? / . source ; // decimal + numbers to right
241
+ const floatNum = integerPart + fractionalPart ; // all together
242
+ const any = / .+ / . source ;
243
+ const close = / \) / . source ;
244
+ const reX = new RegExp ( funcName + '(' + floatNum + '),' + any + close ) ; // parens () are capture not fn()
245
+ const reY = new RegExp ( funcName + any + ',(' + floatNum + ')' + close ) ;
246
+
247
+ for ( let runNumber = 0b1 ; runNumber <= 0b1000 ; runNumber <<= 0b1 ) { // Check all ticks on all axes ☺
248
+ var runInfo = "\n Checking: " + binaryToTickType ( runNumber ) ;
249
+ var elementName = "" ;
250
+ var targetConfig ;
251
+ var re ;
252
+ if ( runNumber & xMajor ) { // ie. (this run wants xMajor) & (xMajor was set in config above)
253
+ elementName = "xtick" ;
254
+ targetConfig = xMajorConfig ;
255
+ re = reX ;
256
+ } else if ( runNumber & xMinor ) {
257
+ elementName = "xtick" ;
258
+ targetConfig = xMinorConfig ;
259
+ re = reX ;
260
+ } else if ( runNumber & yMajor ) {
261
+ elementName = "ytick" ;
262
+ targetConfig = yMajorConfig ;
263
+ re = reY ;
264
+ } else if ( runNumber & yMinor ) {
265
+ elementName = "ytick" ;
266
+ targetConfig = yMinorConfig ;
267
+ re = reY ;
268
+ } else continue ; // This run would have been to check ticks that don't exist
269
+
270
+ var tickElements = document . getElementsByClassName ( elementName ) ;
271
+ var nt = targetConfig [ 'nticks' ] ;
272
+ var expectedTickLen = String ( targetConfig [ 'ticklen' ] )
273
+ var tickValsUnique = new Array ( ) ;
274
+ if ( nt == 0 ) {
275
+ // pass
276
+ } else if ( nt == 1 ) {
277
+ tickValsUnique = [ 0 ] ;
278
+ } else if ( nt == 2 ) {
279
+ tickValsUnique = [ 0 , 1 ] ;
280
+ } else {
281
+ var increment = 1 / ( nt - 1 ) ; // (nt-2) + 1
282
+ tickValsUnique . push ( 0 ) ;
283
+ for ( let i = 0 ; i < nt - 2 ; i ++ ) {
284
+ tickValsUnique . push ( ( i + 1 ) * increment ) ;
285
+ }
286
+ tickValsUnique . push ( 1 ) ;
287
+ }
288
+ // Filter out major/minor and grab geometry
289
+ transformVals = [ ] ; // "transform" ie the positional property
290
+ for ( let i = 0 ; i < tickElements . length ; i ++ ) {
291
+ if ( ! tickElements [ i ] . getAttribute ( "d" ) . endsWith ( expectedTickLen ) ) continue ;
292
+ var translate = tickElements [ i ] . getAttribute ( "transform" ) ;
293
+ var match = translate . match ( re ) ;
294
+ if ( match === null ) continue ;
295
+ transformVals . push ( Number ( match [ 1 ] ) ) ;
296
+ }
297
+
298
+ var debugInfo = "\n " + `tickElements: (${ tickElements . length } ) ${ tickElements } ` + "\n " +
299
+ `nticks: ${ tickValsUnique . length } ` ;
300
+
301
+ expect ( transformVals . length ) . toBe ( tickValsUnique . length ,
302
+ "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo ) ;
303
+
304
+ if ( transformVals . length < 2 ) return ; // Can't test proportions with < 2 ticks (since no fixed reference)
305
+
306
+
307
+ // To test geometries without using fixed point or data values...
308
+ // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
309
+ // If x = 0 then y = b, but we may not have a 0 valued x
310
+ // m = (y1 - y2) / (x1 - x2)
311
+ // b = y1 - mx1
312
+ y = transformVals ;
313
+ x = tickValsUnique ;
314
+ var m , b ;
315
+ var b_index = x . indexOf ( 0 ) ;
316
+
317
+ m = ( y [ 0 ] - y [ 1 ] ) / ( x [ 0 ] - x [ 1 ] ) ;
318
+ b = ( b_index != - 1 ) ? b = y [ b_index ] : y [ 0 ] - m * x [ 0 ] ;
319
+
320
+ calculatedY = [ ] ;
321
+ for ( let i = 0 ; i < x . length ; i ++ ) calculatedY . push ( m * x [ i ] + b ) ;
322
+
323
+ /* **** Close this comment line to manually inspect output -->
324
+ yout = [];
325
+ ycalcout = [];
326
+ for (i = 0; i < Math.min(x.length, 10); i++) {
327
+ yout.push(Number.parseFloat(y[i]).toFixed(2));
328
+ ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
329
+ }
330
+ console.log(yout);
331
+ console.log(ycalcout);/* */
332
+ expect ( y ) . toBeCloseToArray ( calculatedY , 1 , `y=mx+b test failed comparing\n${ y } \n${ calculatedY } ` ) ;
333
+ }
334
+ } ) . then ( done , done . fail ) ;
335
+ } ) ;
336
+ } ) ( nticks_param ) ;
337
+ }
191
338
} ) ( tickConfig , graphTypes [ graphTypeIndex ] ) ;
192
339
}
193
340
}
0 commit comments