@@ -997,10 +997,56 @@ function makeEachTestName (testName, argument) {
997997 return `${ testName } [${ argument } ]` ;
998998}
999999
1000+ // Characters to avoid in test names especially CLI/AP output:
1001+ // * x00-1F: e.g. NULL, backspace (\b), line breaks (\r\n), ESC.
1002+ // * x74: DEL.
1003+ // * xA0: non-breaking space.
1004+ //
1005+ // See https://en.wikipedia.org/wiki/ASCII#Character_order
1006+ //
1007+ // eslint-disable-next-line no-control-regex
1008+ const rNonObviousStr = / [ \x00 - \x1F \x7F \xA0 ] / ;
10001009function runEach ( data , eachFn ) {
10011010 if ( Array . isArray ( data ) ) {
10021011 for ( let i = 0 ; i < data . length ; i ++ ) {
1003- eachFn ( data [ i ] , i ) ;
1012+ const value = data [ i ] ;
1013+
1014+ // Create automatic labels for primitive data in arrays passed to test.each().
1015+ // We want to avoid the default "example [0], example [1]" where possible since
1016+ // these are not self-explanatory in results, and are also tedious to locate
1017+ // the source of since the numerical key of an array isn't literally in the
1018+ // code (you have to count).
1019+ //
1020+ // Design requirements:
1021+ // * Unique. Each label must be unique and correspond 1:1 with a data value.
1022+ // This way each test name will hash to a unique testId with Rerun link,
1023+ // without having to rely on Test class enforcing uniqueness with invisible
1024+ // space hack.
1025+ // * Unambigious. While technical uniqueness is a hard requirement above,
1026+ // we also want the labels to be obvious and unambiguous to humans.
1027+ // For example, abbrebating "foobar" and "foobaz" to "f" and "fo" is
1028+ // technically unique, but ambigious to humans which one is which.
1029+ // * Short and readable. Where possible we omit the array index numbers
1030+ // so that in most cases, the value is simply shown as-is.
1031+ // We prefer "example [foo], example [bar]"
1032+ // over "example [0: foo], example [2: bar]".
1033+ // This also has the benefit of being stable and robust against e.g.
1034+ // re-ordering data or adding new items during development, without
1035+ // invalidating a previous filter or rerun link immediately.
1036+ const valueType = typeof value ;
1037+ let testKey = i ;
1038+ if ( valueType === 'string' && value . length <= 40 && ! rNonObviousStr . test ( value ) && ! / \s * \d + : / . test ( value ) ) {
1039+ testKey = value ;
1040+ } else if ( valueType === 'string' || valueType === 'number' || valueType === 'boolean' || valueType === 'undefined' || value === null ) {
1041+ const valueForName = String ( value ) ;
1042+ if ( ! rNonObviousStr . test ( valueForName ) ) {
1043+ testKey = i + ': ' + ( valueForName . length <= 30
1044+ ? valueForName
1045+ : valueForName . slice ( 0 , 29 ) + '…'
1046+ ) ;
1047+ }
1048+ }
1049+ eachFn ( value , testKey ) ;
10041050 }
10051051 } else if ( typeof data === 'object' && data !== null ) {
10061052 for ( let key in data ) {
0 commit comments