diff --git a/.editorconfig b/.editorconfig index 991900b..818e072 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,3 @@ -# http://editorconfig.org root = true [*] @@ -9,14 +8,6 @@ indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true -[*.md] +[{**/{actual,fixtures,expected,templates}/**,*.md}] trim_trailing_whitespace = false -insert_final_newline = false - -[test/**] -trim_trailing_whitespace = false -insert_final_newline = false - -[templates/**] -trim_trailing_whitespace = false -insert_final_newline = false +insert_final_newline = false \ No newline at end of file diff --git a/.eslintrc b/.eslintrc.json similarity index 95% rename from .eslintrc rename to .eslintrc.json index 7b5d047..948dbdb 100644 --- a/.eslintrc +++ b/.eslintrc.json @@ -34,6 +34,7 @@ "handle-callback-err": [2, "^(err|error)$" ], "indent": [2, 2, { "SwitchCase": 1 }], "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "keyword-spacing": [2, { "before": true, "after": true }], "new-cap": [2, { "newIsCap": true, "capIsNew": false }], "new-parens": 2, "no-array-constructor": 2, @@ -49,7 +50,6 @@ "no-dupe-keys": 2, "no-duplicate-case": 2, "no-empty-character-class": 2, - "no-empty-label": 2, "no-eval": 2, "no-ex-assign": 2, "no-extend-native": 2, @@ -71,7 +71,7 @@ "no-multi-spaces": 2, "no-multi-str": 2, "no-multiple-empty-lines": [2, { "max": 1 }], - "no-native-reassign": 2, + "no-native-reassign": 0, "no-negated-in-lhs": 2, "no-new": 2, "no-new-func": 2, @@ -108,13 +108,10 @@ "radix": 2, "semi": [2, "always"], "semi-spacing": [2, { "before": false, "after": true }], - "space-after-keywords": [2, "always"], "space-before-blocks": [2, "always"], "space-before-function-paren": [2, "never"], - "space-before-keywords": [2, "always"], "space-in-parens": [2, "never"], "space-infix-ops": 2, - "space-return-throw-case": 2, "space-unary-ops": [2, { "words": true, "nonwords": false }], "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], "use-isnan": 2, diff --git a/.gitignore b/.gitignore index 22e91b0..7988154 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,21 @@ +# always ignore files +*.DS_Store *.sublime-* -_gh_pages -bower_components + +# test related, or directories generated by tests +test/actual +actual +coverage + +# npm node_modules npm-debug.log + +# misc +_gh_pages +benchmark +bower_components +vendor temp -test/actual tmp TODO.md -vendor -*.DS_Store -mm.* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index d6e658e..04a029e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,12 @@ sudo: false language: node_js node_js: - - "stable" - - "0.12" - - "0.10" + - node + - '6' + - '5' + - '0.12' matrix: fast_finish: true allow_failures: - - node_js: "0.10" + - node_js: '4' + - node_js: '0.12' diff --git a/.verb.md b/.verb.md index 27eb205..9104fc8 100644 --- a/.verb.md +++ b/.verb.md @@ -1,66 +1,83 @@ -# {%= name %} {%= badge("fury") %} {%= badge("travis") %} +## Usage + +Expands numbers and letters, optionally using a `step` as the last argument. _(Numbers may be defined as JavaScript numbers or strings)_. -> {%= description %} +```js +var fill = require('{%= name %}'); -## Install +console.log(fill('a', 'e')); +//=> ['a', 'b', 'c', 'd', 'e'] -{%= include("install-npm", {save: true}) %} +console.log(fill(0, 25, 5)); +//=> [ 0, 5, 10, 15, 20, 25 ] - +console.log(fill('a', 'e', {toRegex: true})); +//=> '[a-e]' -## Usage +console.log(fill('a', 'z', 3, {toRegex: true})); +//=> 'a|d|g|j|m|p|s|v|y' + +console.log(fill('1', '100', {toRegex: true})); +//=> '[1-9]|[1-9][0-9]|100' +``` + +Create regex-compatible ranges (returns a string, which can be used however you need to create a regex): ```js -var range = require('{%= name %}'); +console.log(fill('a', 'e', {toRegex: true})); +//=> '[a-e]' -range('a', 'e'); -//=> ['a', 'b', 'c', 'd', 'e'] +console.log(fill('a', 'z', 3, {toRegex: true})); +//=> 'a|d|g|j|m|p|s|v|y' + +console.log(fill('1', '100', {toRegex: true})); +//=> '[1-9]|[1-9][0-9]|100' ``` **Params** ```js -range(start, stop, step, options, fn); +fill(start, stop, step, options, fn); ``` - `start`: **{String|Number}** the number or letter to start with - `end`: **{String|Number}** the number or letter to end with - `step`: **{String|Number}** optionally pass the step to use. works for letters or numbers. - `options`: **{Object}**: - + `makeRe`: return a regex-compatible string (still returned as an array for consistency) + + `toRegex`: return a regex-compatible string (still returned as an array for consistency) + `step`: pass the step on the options as an alternative to passing it as an argument - + `silent`: `true` by default, set to false to throw errors for invalid ranges. - - `fn`: **{Function}** optionally [pass a function](#custom-function) to modify each character + + `strict`: `undefined` by default, set to true to throw errors on invalid ranges. + - `fn`: **{Function}** optionally [pass a function](#custom-function) to modify each character. This can also be defined as `options.transform` **Examples** ```js -range(1, 3) +fill(1, 3) //=> ['1', '2', '3'] -range('1', '3') +fill('1', '3') //=> ['1', '2', '3'] -range('0', '-5') +fill('0', '-5') //=> [ '0', '-1', '-2', '-3', '-4', '-5' ] -range(-9, 9, 3) +fill(-9, 9, 3) //=> [ '-9', '-6', '-3', '0', '3', '6', '9' ]) -range('-1', '-10', '-2') +fill('-1', '-10', '-2') //=> [ '-1', '-3', '-5', '-7', '-9' ] -range('1', '10', '2') +fill('1', '10', '2') //=> [ '1', '3', '5', '7', '9' ] -range('a', 'e') +fill('a', 'e') //=> ['a', 'b', 'c', 'd', 'e'] -range('a', 'e', 2) +fill('a', 'e', 2) //=> ['a', 'c', 'e'] -range('A', 'E', 2) +fill('A', 'E', 2) //=> ['A', 'C', 'E'] ``` @@ -69,25 +86,25 @@ range('A', 'E', 2) When an invalid range is passed, `null` is returned. ```js -range('1.1', '2'); +fill('1.1', '2'); // decimals not supported in ranges //=> null -range('a', '2'); +fill('a', '2'); // unmatched values //=> null -range(1, 10, 'foo'); +fill(1, 10, 'foo'); // invalid step //=> null ``` -If you want errors to be throw, pass `silent: false` on the options: +If you want errors to be throw, set `options.strict` to true. ### Custom function -Optionally pass a custom function as the third or fourth argument: +Optionally pass a custom function as the third or fourth argument or on `options.transform`. ```js -range('a', 'e', function (val, isNumber, pad, i) { +fill('a', 'e', function (val, isNumber, pad, i) { if (!isNumber) { return String.fromCharCode(val) + i; } @@ -95,162 +112,3 @@ range('a', 'e', function (val, isNumber, pad, i) { }); //=> ['a0', 'b1', 'c2', 'd3', 'e4'] ``` - -### Special characters - -A special character may be passed as the third arg instead of a step increment. These characters can be pretty useful for brace expansion, creating file paths, test fixtures and similar use case. - -```js -range('a', 'z', SPECIAL_CHARACTER_HERE); -``` - -**Supported characters** - - - `+`: repeat the given string `n` times - - `|`: create a regex-ready string, instead of an array - - `>`: join values to single array element - - `?`: randomize the given pattern using [randomatic] - -#### plus - -Character: _(`+`)_ - -Repeat the first argument the number of times passed on the second argument. - -**Examples:** - -```js -range('a', 3, '+'); -//=> ['a', 'a', 'a'] - -range('abc', 2, '+'); -//=> ['abc', 'abc'] -``` - -#### pipe and tilde - -Characters: _(`|` and `~`)_ - -Creates a regex-capable string (either a logical `or` or a character class) from the expanded arguments. - -**Examples:** - -```js -range('a', 'c', '|'); -//=> ['(a|b|c)' - -range('a', 'c', '~'); -//=> ['[a-c]' - -range('a', 'z', '|5'); -//=> ['(a|f|k|p|u|z)' -``` - -**Automatic separator correction** - -To avoid this error: - -> `Range out of order in character class` - -Fill-range detects invalid sequences and uses the correct syntax. For example: - -**invalid** (regex) - -If you pass these: - -```js -range('a', 'z', '~5'); -// which would result in this -//=> ['[a-f-k-p-u-z]'] - -range('10', '20', '~'); -// which would result in this -//=> ['[10-20]'] -``` - -**valid** (regex) - -fill-range corrects them to this: - -```js -range('a', 'z', '~5'); -//=> ['(a|f|k|p|u|z)' - -range('10', '20', '~'); -//=> ['(10-20)' -``` - -#### angle bracket - -Character: _(`>`)_ - -Joins all values in the returned array to a single value. - -**Examples:** - -```js -range('a', 'e', '>'); -//=> ['abcde'] - -range('5', '8', '>'); -//=> ['5678'] - -range('2', '20', '2>'); -//=> ['2468101214161820'] -``` - - -#### question mark - -Character: _(`?`)_ - -Uses [randomatic] to generate randomized alpha, numeric, or alpha-numeric patterns based on the provided arguments. - -**Examples:** - -_(actual results would obviously be randomized)_ - -Generate a 5-character, uppercase, alphabetical string: - -```js -range('A', 5, '?'); -//=> ['NSHAK'] -``` - -Generate a 5-digit random number: - -```js -range('0', 5, '?'); -//=> ['36583'] -``` - -Generate a 10-character alpha-numeric string: - -```js -range('A0', 10, '?'); -//=> ['5YJD60VQNN'] -``` - -See the [randomatic] repo for all available options and or to create issues or feature requests related to randomization. - -## Other useful libs -{%= related(verb.related.list) %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} - -## License -{%= copyright({year: 2014}) %} -{%= license() %} - -*** - -{%= include("footer") %} - -{%= reflinks(['randomatic', 'expand-range', 'micromatch', 'braces']) %} diff --git a/LICENSE b/LICENSE index fa30c4c..842218c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2015, Jon Schlinkert. +Copyright (c) 2014-2016, Jon Schlinkert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c69694a..4613fd3 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,136 @@ -# fill-range [![NPM version](https://badge.fury.io/js/fill-range.svg)](http://badge.fury.io/js/fill-range) [![Build Status](https://travis-ci.org/jonschlinkert/fill-range.svg)](https://travis-ci.org/jonschlinkert/fill-range) +# fill-range [![NPM version](https://img.shields.io/npm/v/fill-range.svg?style=flat)](https://www.npmjs.com/package/fill-range) [![NPM downloads](https://img.shields.io/npm/dm/fill-range.svg?style=flat)](https://npmjs.org/package/fill-range) [![Build Status](https://img.shields.io/travis/jonschlinkert/fill-range.svg?style=flat)](https://travis-ci.org/jonschlinkert/fill-range) -> Fill in a range of numbers or letters, optionally passing an increment or multiplier to use. +> Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex` -## Install with [npm](npmjs.org) - -```bash -npm i fill-range --save -``` - - +## Table of Contents +- [Install](#install) - [Usage](#usage) * [Invalid ranges](#invalid-ranges) * [Custom function](#custom-function) - * [Special characters](#special-characters) - + [plus](#plus) - + [pipe and tilde](#pipe-and-tilde) - + [angle bracket](#angle-bracket) - + [question mark](#question-mark) -- [Other useful libs](#other-useful-libs) -- [Running tests](#running-tests) -- [Contributing](#contributing) -- [Author](#author) -- [License](#license) +- [About](#about) + * [Related projects](#related-projects) + * [Contributing](#contributing) + * [Building docs](#building-docs) + * [Running tests](#running-tests) + * [Author](#author) + * [License](#license) + +_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ + +## Install -_(Table of contents generated by [verb])_ +Install with [npm](https://www.npmjs.com/): - +```sh +$ npm install --save fill-range +``` ## Usage +Expands numbers and letters, optionally using a `step` as the last argument. _(Numbers may be defined as JavaScript numbers or strings)_. + ```js -var range = require('fill-range'); +var fill = require('fill-range'); -range('a', 'e'); +console.log(fill('a', 'e')); //=> ['a', 'b', 'c', 'd', 'e'] + +console.log(fill(0, 25, 5)); +//=> [ 0, 5, 10, 15, 20, 25 ] + +console.log(fill('a', 'e', {toRegex: true})); +//=> '[a-e]' + +console.log(fill('a', 'z', 3, {toRegex: true})); +//=> 'a|d|g|j|m|p|s|v|y' + +console.log(fill('1', '100', {toRegex: true})); +//=> '[1-9]|[1-9][0-9]|100' ``` -**Params** +Create regex-compatible ranges (returns a string, which can be used however you need to create a regex): ```js -range(start, stop, step, options, fn); +console.log(fill('a', 'e', {toRegex: true})); +//=> '[a-e]' + +console.log(fill('a', 'z', 3, {toRegex: true})); +//=> 'a|d|g|j|m|p|s|v|y' + +console.log(fill('1', '100', {toRegex: true})); +//=> '[1-9]|[1-9][0-9]|100' ``` - - `start`: **{String|Number}** the number or letter to start with - - `end`: **{String|Number}** the number or letter to end with - - `step`: **{String|Number}** optionally pass the step to use. works for letters or numbers. - - `options`: **{Object}**: - + `makeRe`: return a regex-compatible string (still returned as an array for consistency) - + `step`: pass the step on the options as an alternative to passing it as an argument - + `silent`: `true` by default, set to false to throw errors for invalid ranges. - - `fn`: **{Function}** optionally [pass a function](#custom-function) to modify each character +**Params** + +```js +fill(start, stop, step, options, fn); +``` +* `start`: **{String|Number}** the number or letter to start with +* `end`: **{String|Number}** the number or letter to end with +* `step`: **{String|Number}** optionally pass the step to use. works for letters or numbers. +* `options`: **{Object}**: + - `toRegex`: return a regex-compatible string (still returned as an array for consistency) + - `step`: pass the step on the options as an alternative to passing it as an argument + - `strict`: `undefined` by default, set to true to throw errors on invalid ranges. +* `fn`: **{Function}** optionally [pass a function](#custom-function) to modify each character. This can also be defined as `options.transform` **Examples** ```js -range(1, 3) +fill(1, 3) //=> ['1', '2', '3'] -range('1', '3') +fill('1', '3') //=> ['1', '2', '3'] -range('0', '-5') +fill('0', '-5') //=> [ '0', '-1', '-2', '-3', '-4', '-5' ] -range(-9, 9, 3) +fill(-9, 9, 3) //=> [ '-9', '-6', '-3', '0', '3', '6', '9' ]) -range('-1', '-10', '-2') +fill('-1', '-10', '-2') //=> [ '-1', '-3', '-5', '-7', '-9' ] -range('1', '10', '2') +fill('1', '10', '2') //=> [ '1', '3', '5', '7', '9' ] -range('a', 'e') +fill('a', 'e') //=> ['a', 'b', 'c', 'd', 'e'] -range('a', 'e', 2) +fill('a', 'e', 2) //=> ['a', 'c', 'e'] -range('A', 'E', 2) +fill('A', 'E', 2) //=> ['A', 'C', 'E'] ``` ### Invalid ranges -When an invalid range is passed, `null` is returned. +When an invalid range is passed, `null` is returned. ```js -range('1.1', '2'); +fill('1.1', '2'); // decimals not supported in ranges //=> null -range('a', '2'); +fill('a', '2'); // unmatched values //=> null -range(1, 10, 'foo'); +fill(1, 10, 'foo'); // invalid step //=> null ``` -If you want errors to be throw, pass `silent: false` on the options: - +If you want errors to be throw, set `options.strict` to true. ### Custom function -Optionally pass a custom function as the third or fourth argument: +Optionally pass a custom function as the third or fourth argument or on `options.transform`. ```js -range('a', 'e', function (val, isNumber, pad, i) { +fill('a', 'e', function (val, isNumber, pad, i) { if (!isNumber) { return String.fromCharCode(val) + i; } @@ -116,175 +139,49 @@ range('a', 'e', function (val, isNumber, pad, i) { //=> ['a0', 'b1', 'c2', 'd3', 'e4'] ``` -### Special characters - -A special character may be passed as the third arg instead of a step increment. These characters can be pretty useful for brace expansion, creating file paths, test fixtures and similar use case. - -```js -range('a', 'z', SPECIAL_CHARACTER_HERE); -``` - -**Supported characters** - - - `+`: repeat the given string `n` times - - `|`: create a regex-ready string, instead of an array - - `>`: join values to single array element - - `?`: randomize the given pattern using [randomatic] - -#### plus - -Character: _(`+`)_ - -Repeat the first argument the number of times passed on the second argument. - -**Examples:** - -```js -range('a', 3, '+'); -//=> ['a', 'a', 'a'] - -range('abc', 2, '+'); -//=> ['abc', 'abc'] -``` - -#### pipe and tilde - -Characters: _(`|` and `~`)_ - -Creates a regex-capable string (either a logical `or` or a character class) from the expanded arguments. - -**Examples:** - -```js -range('a', 'c', '|'); -//=> ['(a|b|c)' - -range('a', 'c', '~'); -//=> ['[a-c]' - -range('a', 'z', '|5'); -//=> ['(a|f|k|p|u|z)' -``` - -**Automatic separator correction** - -To avoid this error: - -> `Range out of order in character class` - -Fill-range detects invalid sequences and uses the correct syntax. For example: - -**invalid** (regex) - -If you pass these: - -```js -range('a', 'z', '~5'); -// which would result in this -//=> ['[a-f-k-p-u-z]'] - -range('10', '20', '~'); -// which would result in this -//=> ['[10-20]'] -``` - -**valid** (regex) - -fill-range corrects them to this: - -```js -range('a', 'z', '~5'); -//=> ['(a|f|k|p|u|z)' - -range('10', '20', '~'); -//=> ['(10-20)' -``` - -#### angle bracket - -Character: _(`>`)_ - -Joins all values in the returned array to a single value. - -**Examples:** - -```js -range('a', 'e', '>'); -//=> ['abcde'] - -range('5', '8', '>'); -//=> ['5678'] - -range('2', '20', '2>'); -//=> ['2468101214161820'] -``` - - -#### question mark - -Character: _(`?`)_ +## About -Uses [randomatic] to generate randomized alpha, numeric, or alpha-numeric patterns based on the provided arguments. +### Related projects -**Examples:** +* [braces](https://www.npmjs.com/package/braces): Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces… [more](https://github.com/jonschlinkert/braces) | [homepage](https://github.com/jonschlinkert/braces "Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces specification.") +* [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See… [more](https://github.com/jonschlinkert/expand-range) | [homepage](https://github.com/jonschlinkert/expand-range "Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See the benchmarks. Used by micromatch.") +* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.") +* [to-regex-range](https://www.npmjs.com/package/to-regex-range): Returns a regex-compatible range from two numbers, min and max, with 855,412 generated unit tests… [more](https://github.com/jonschlinkert/to-regex-range) | [homepage](https://github.com/jonschlinkert/to-regex-range "Returns a regex-compatible range from two numbers, min and max, with 855,412 generated unit tests to validate it's accuracy! Useful for creating regular expressions to validate numbers, ranges, years, etc. Returns a string, allowing the returned value to ") -_(actual results would obviously be randomized)_ +### Contributing -Generate a 5-character, uppercase, alphabetical string: +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). -```js -range('A', 5, '?'); -//=> ['NSHAK'] -``` - -Generate a 5-digit random number: +### Building docs -```js -range('0', 5, '?'); -//=> ['36583'] -``` +_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_ -Generate a 10-character alpha-numeric string: +To generate the readme and API documentation with [verb](https://github.com/verbose/verb): -```js -range('A0', 10, '?'); -//=> ['5YJD60VQNN'] +```sh +$ npm install -g verb verb-generate-readme && verb ``` -See the [randomatic] repo for all available options and or to create issues or feature requests related to randomization. - -## Other useful libs - * [micromatch](https://github.com/jonschlinkert/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. Just use `micromatch.isMatch()` instead of `minimatch()`, or use `micromatch()` instead of `multimatch()`. - * [expand-range](https://github.com/jonschlinkert/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See the benchmarks. Used by micromatch. - * [braces](https://github.com/jonschlinkert/braces): Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces specification. - * [is-glob](https://github.com/jonschlinkert/is-glob): Returns `true` if the given string looks like a glob pattern. +### Running tests -## Running tests Install dev dependencies: -```bash -npm i -d && npm test +```sh +$ npm install -d && npm test ``` -## Contributing -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/fill-range/issues) - -## Author +### Author **Jon Schlinkert** -+ [github/jonschlinkert](https://github.com/jonschlinkert) -+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) -## License -Copyright (c) 2014-2015 Jon Schlinkert -Released under the MIT license +### License -*** +Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT license](https://github.com/jonschlinkert/fill-range/blob/master/LICENSE). -_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on April 07, 2015._ +*** -[randomatic]: https://github.com/jonschlinkert/randomatic -[expand-range]: https://github.com/jonschlinkert/expand-range -[micromatch]: https://github.com/jonschlinkert/micromatch -[braces]: https://github.com/jonschlinkert/braces \ No newline at end of file +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.30, on September 15, 2016._ \ No newline at end of file diff --git a/benchmark/check.js b/benchmark/check.js deleted file mode 100644 index 31faa84..0000000 --- a/benchmark/check.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var chalk = require('chalk'); -var path = require('path'); - -/** - * Sanity check. run to ensure that all fns return a correct - * result. - */ - -fs.readdirSync(__dirname + '/code').forEach(function (fp) { - var fn = require(path.resolve(__dirname, 'code', fp)); - var name = path.basename(fp, path.extname(fp)); - - fs.readdirSync(__dirname + '/fixtures').forEach(function (fixture) { - fixture = path.resolve(__dirname, 'fixtures', fixture); - if (/.*(or|ss)\.js$/.test(fixture)) { - console.log(chalk.bold(name) + ':', fn.apply(null, require(fixture))); - } - }); -}); diff --git a/benchmark/fixtures/1-100-logical-or.js b/benchmark/fixtures/1-100-logical-or.js deleted file mode 100644 index 6ab4051..0000000 --- a/benchmark/fixtures/1-100-logical-or.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['1', '100', '1~']; diff --git a/benchmark/fixtures/a-z-character-class.js b/benchmark/fixtures/a-z-character-class.js deleted file mode 100644 index a3e9306..0000000 --- a/benchmark/fixtures/a-z-character-class.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a', 'z', '~']; diff --git a/benchmark/fixtures/options-num.js b/benchmark/fixtures/options-num.js deleted file mode 100644 index 351473a..0000000 --- a/benchmark/fixtures/options-num.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a', 'z', {step: '5|'}]; diff --git a/benchmark/fixtures/options-str.js b/benchmark/fixtures/options-str.js deleted file mode 100644 index e11271b..0000000 --- a/benchmark/fixtures/options-str.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['a', 'z', {step: '~'}]; diff --git a/benchmark/fixtures/random.js b/benchmark/fixtures/random.js deleted file mode 100644 index 0bd601b..0000000 --- a/benchmark/fixtures/random.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ['aA0', '25', '?']; diff --git a/benchmark/index.js b/benchmark/index.js index ed80d2b..2888f6e 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -1,11 +1,30 @@ 'use strict'; +var path = require('path'); +var util = require('util'); +var cyan = require('ansi-cyan'); +var argv = require('yargs-parser')(process.argv.slice(2)); var Suite = require('benchmarked'); -var suite = new Suite({ - result: false, - fixtures: 'fixtures/*.js', - add: 'code/*.js', - cwd: __dirname -}); - -suite.run(); + +function run(code, fixtures) { + var suite = new Suite({ + cwd: __dirname, + fixtures: `fixtures/${fixtures}.js`, + code: `code/${code}*.js` + }); + + if (argv.dry) { + suite.dryRun(function(code, fixture) { + if (/special/.test(fixture.stem)) return; + console.log(cyan('%s > %s'), code.key, fixture.key); + var args = require(fixture.path); + var res = code.run.apply(null, args).length; + console.log(util.inspect(res, null, 10)); + console.log(); + }); + } else { + suite.run(); + } +} + +run(argv._[0] || '*', argv._[1] || '**/{1,2,a}*'); diff --git a/examples.js b/examples.js new file mode 100644 index 0000000..44073ef --- /dev/null +++ b/examples.js @@ -0,0 +1,13 @@ +var fill = require('./'); + +console.log(fill(0, 25, 5)); +//=> [ 0, 5, 10, 15, 20, 25 ] + +console.log(fill('a', 'e', {toRegex: true})); +//=> '[a-e]' + +console.log(fill('a', 'z', 3, {toRegex: true})); +//=> 'a|d|g|j|m|p|s|v|y' + +console.log(fill('1', '100', {toRegex: true})); +//=> '[1-9]|[1-9][0-9]|100' diff --git a/index.js b/index.js index 5657051..794acad 100644 --- a/index.js +++ b/index.js @@ -7,402 +7,193 @@ 'use strict'; -var isObject = require('isobject'); +var util = require('util'); var isNumber = require('is-number'); -var randomize = require('randomatic'); -var repeatStr = require('repeat-string'); -var repeat = require('repeat-element'); - -/** - * Expose `fillRange` - */ - -module.exports = fillRange; +var extend = require('extend-shallow'); +var repeat = require('repeat-string'); +var toRegex = require('to-regex-range'); /** * Return a range of numbers or letters. * - * @param {String} `a` Start of the range - * @param {String} `b` End of the range + * @param {String} `start` Start of the range + * @param {String} `stop` End of the range * @param {String} `step` Increment or decrement to use. * @param {Function} `fn` Custom function to modify each element in the range. * @return {Array} */ -function fillRange(a, b, step, options, fn) { - if (a == null || b == null) { - throw new Error('fill-range expects the first and second args to be strings.'); - } - - if (typeof step === 'function') { - fn = step; options = {}; step = null; - } - - if (typeof options === 'function') { - fn = options; options = {}; +function fillRange(start, stop, step, options) { + if (typeof start === 'undefined') { + return []; } - if (isObject(step)) { - options = step; step = ''; - } - - var expand, regex = false, sep = ''; - var opts = options || {}; - - if (typeof opts.silent === 'undefined') { - opts.silent = true; + if (typeof stop === 'undefined' || start === stop) { + // special case, for handling negative zero + var isString = typeof start === 'string'; + if (isNumber(start) && !toNumber(start)) { + return [isString ? '0' : 0]; + } + return [start]; } - step = step || opts.step; - - // store a ref to unmodified arg - var origA = a, origB = b; - - b = (b.toString() === '-0') ? 0 : b; - - if (opts.optimize || opts.makeRe) { - step = step ? (step += '~') : step; - expand = true; - regex = true; - sep = '~'; + if (typeof step !== 'number' && typeof step !== 'string') { + options = step; + step = null; } - // handle special step characters - if (typeof step === 'string') { - var match = stepRe().exec(step); - - if (match) { - var i = match.index; - var m = match[0]; - - // repeat string - if (m === '+') { - return repeat(a, b); - - // randomize a, `b` times - } else if (m === '?') { - return [randomize(a, b)]; - - // expand right, no regex reduction - } else if (m === '>') { - step = step.substr(0, i) + step.substr(i + 1); - expand = true; - - // expand to an array, or if valid create a reduced - // string for a regex logic `or` - } else if (m === '|') { - step = step.substr(0, i) + step.substr(i + 1); - expand = true; - regex = true; - sep = m; - - // expand to an array, or if valid create a reduced - // string for a regex range - } else if (m === '~') { - step = step.substr(0, i) + step.substr(i + 1); - expand = true; - regex = true; - sep = m; - } - } else if (!isNumber(step)) { - if (!opts.silent) { - throw new TypeError('fill-range: invalid step.'); - } - return null; - } + if (typeof options === 'function') { + options = { transform: options }; } - if (/[.&*()[\]^%$#@!]/.test(a) || /[.&*()[\]^%$#@!]/.test(b)) { - if (!opts.silent) { - throw new RangeError('fill-range: invalid range arguments.'); + var opts = extend({step: step}, options); + if (opts.step && !isValidNumber(opts.step)) { + if (opts.strict === true) { + throw new TypeError('expected options.step to be a number'); } - return null; + return []; } - // has neither a letter nor number, or has both letters and numbers - // this needs to be after the step logic - if (!noAlphaNum(a) || !noAlphaNum(b) || hasBoth(a) || hasBoth(b)) { - if (!opts.silent) { - throw new RangeError('fill-range: invalid range arguments.'); + opts.isNumber = isValidNumber(start) && isValidNumber(stop); + if (!opts.isNumber && !isValid(start, stop)) { + if (opts.strict === true) { + throw new RangeError('invalid range arguments: ' + util.inspect([start, stop])); } - return null; + return []; } - // validate arguments - var isNumA = isNumber(zeros(a)); - var isNumB = isNumber(zeros(b)); + opts.isPadded = isPadded(start) || isPadded(stop); + opts.toString = opts.stringify + || typeof opts.step === 'string' + || typeof start === 'string' + || typeof stop === 'string' + || !opts.isNumber; - if ((!isNumA && isNumB) || (isNumA && !isNumB)) { - if (!opts.silent) { - throw new TypeError('fill-range: first range argument is incompatible with second.'); - } - return null; + if (opts.isPadded) { + opts.maxLength = Math.max(String(start).length, String(stop).length); } - // by this point both are the same, so we - // can use A to check going forward. - var isNum = isNumA; - var num = formatStep(step); - - // is the range alphabetical? or numeric? - if (isNum) { - // if numeric, coerce to an integer - a = +a; b = +b; - } else { - // otherwise, get the charCode to expand alpha ranges - a = a.charCodeAt(0); - b = b.charCodeAt(0); - } + // support legacy minimatch/fill-range options + if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize; + if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe; + return expand(start, stop, opts); +} - // is the pattern descending? - var isDescending = a > b; +function expand(start, stop, options) { + var a = options.isNumber ? toNumber(start) : start.charCodeAt(0); + var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0); - // don't create a character class if the args are < 0 - if (a < 0 || b < 0) { - expand = false; - regex = false; + var step = Math.abs(toNumber(options.step)) || 1; + if (options.toRegex && step === 1) { + return toRange(a, b, options); } - // detect padding - var padding = isPadded(origA, origB); - var res, pad, arr = []; - var ii = 0; + var zero = {greater: [], lesser: []}; + var asc = a < b; + var arr = new Array(Math.round((asc ? b - a : a - b) / step)); + var idx = 0; - // character classes, ranges and logical `or` - if (regex) { - if (shouldExpand(a, b, num, isNum, padding, opts)) { - // make sure the correct separator is used - if (sep === '|' || sep === '~') { - sep = detectSeparator(a, b, num, isNum, isDescending); - } - return wrap([origA, origB], sep, opts); + while (asc ? a <= b : a >= b) { + var val = options.isNumber ? a : String.fromCharCode(a); + if (options.toRegex && (val >= 0 || !options.isNumber)) { + zero.greater.push(val); + } else { + zero.lesser.push(Math.abs(val)); } - } - while (isDescending ? (a >= b) : (a <= b)) { - if (padding && isNum) { - pad = padding(a); + if (options.isPadded) { + val = zeros(val, options); } - // custom function - if (typeof fn === 'function') { - res = fn(a, isNum, pad, ii++); - - // letters - } else if (!isNum) { - if (regex && isInvalidChar(a)) { - res = null; - } else { - res = String.fromCharCode(a); - } + if (options.toString) { + val = String(val); + } - // numbers + if (typeof options.transform === 'function') { + arr[idx++] = options.transform(val, a, b, step, idx, arr, options); } else { - res = formatPadding(a, pad); + arr[idx++] = val; } - // add result to the array, filtering any nulled values - if (res !== null) arr.push(res); - - // increment or decrement - if (isDescending) { - a -= num; + if (asc) { + a += step; } else { - a += num; + a -= step; } } - // now that the array is expanded, we need to handle regex - // character classes, ranges or logical `or` that wasn't - // already handled before the loop - if ((regex || expand) && !opts.noexpand) { - // make sure the correct separator is used - if (sep === '|' || sep === '~') { - sep = detectSeparator(a, b, num, isNum, isDescending); - } - if (arr.length === 1 || a < 0 || b < 0) { return arr; } - return wrap(arr, sep, opts); + if (options.toRegex === true) { + return toSequence(arr, zero); } - return arr; } -/** - * Wrap the string with the correct regex - * syntax. - */ - -function wrap(arr, sep, opts) { - if (sep === '~') { sep = '-'; } - var str = arr.join(sep); - var pre = opts && opts.regexPrefix; - - // regex logical `or` - if (sep === '|') { - str = pre ? pre + str : str; - str = '(' + str + ')'; +function toRange(a, b, options) { + if (options.isNumber) { + return toRegex(a, b); } - - // regex character class - if (sep === '-') { - str = (pre && pre === '^') - ? pre + str - : str; - str = '[' + str + ']'; - } - return [str]; + var start = String.fromCharCode(Math.min(a, b)); + var stop = String.fromCharCode(Math.max(a, b)); + return '[' + start + '-' + stop + ']'; } -/** - * Check for invalid characters - */ - -function isCharClass(a, b, step, isNum, isDescending) { - if (isDescending) { return false; } - if (isNum) { return a <= 9 && b <= 9; } - if (a < b) { return step === 1; } - return false; -} - -/** - * Detect the correct separator to use - */ - -function shouldExpand(a, b, num, isNum, padding, opts) { - if (isNum && (a > 9 || b > 9)) { return false; } - return !padding && num === 1 && a < b; -} - -/** - * Detect the correct separator to use - */ - -function detectSeparator(a, b, step, isNum, isDescending) { - var isChar = isCharClass(a, b, step, isNum, isDescending); - if (!isChar) { - return '|'; +function toSequence(arr, zeros) { + var greater = '', lesser = ''; + if (zeros.greater.length) { + greater = zeros.greater.join('|'); } - return '~'; -} - -/** - * Correctly format the step based on type - */ - -function formatStep(step) { - return Math.abs(step >> 0) || 1; -} - -/** - * Format padding, taking leading `-` into account - */ - -function formatPadding(ch, pad) { - var res = pad ? pad + ch : ch; - if (pad && ch.toString().charAt(0) === '-') { - res = '-' + pad + ch.toString().substr(1); + if (zeros.lesser.length) { + lesser = '-(' + zeros.lesser.join('|') + ')'; } - return res.toString(); -} - -/** - * Check for invalid characters - */ - -function isInvalidChar(str) { - var ch = toStr(str); - return ch === '\\' - || ch === '[' - || ch === ']' - || ch === '^' - || ch === '(' - || ch === ')' - || ch === '`'; -} - -/** - * Convert to a string from a charCode - */ - -function toStr(ch) { - return String.fromCharCode(ch); + if (greater && lesser) { + return greater + '|' + lesser; + } + return greater || lesser; } - -/** - * Step regex - */ - -function stepRe() { - return /\?|>|\||\+|\~/g; +function zeros(val, options) { + if (options.isPadded) { + var str = String(val); + var len = str.length; + var dash = ''; + if (str.charAt(0) === '-') { + dash = '-'; + str = str.slice(1); + } + var diff = options.maxLength - len; + var pad = repeat('0', diff); + val = (dash + pad + str); + } + if (options.stringify) { + return String(val); + } + return val; } -/** - * Return true if `val` has either a letter - * or a number - */ - -function noAlphaNum(val) { - return /[a-z0-9]/i.test(val); +function toNumber(val) { + return val >> 0; } -/** - * Return true if `val` has both a letter and - * a number (invalid) - */ - -function hasBoth(val) { - return /[a-z][0-9]|[0-9][a-z]/i.test(val); +function isPadded(str) { + return /^-*0\d+/.test(str); } -/** - * Normalize zeros for checks - */ - -function zeros(val) { - if (/^-*0+$/.test(val.toString())) { - return '0'; - } - return val; +function isValid(min, max) { + return (isValidNumber(min) || isValidLetter(min)) + && (isValidNumber(max) || isValidLetter(max)); } -/** - * Return true if `val` has leading zeros, - * or a similar valid pattern. - */ - -function hasZeros(val) { - return /[^.]\.|^-*0+[0-9]/.test(val); +function isValidLetter(ch) { + return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch); } -/** - * If the string is padded, returns a curried function with - * the a cached padding string, or `false` if no padding. - * - * @param {*} `origA` String or number. - * @return {String|Boolean} - */ - -function isPadded(origA, origB) { - if (hasZeros(origA) || hasZeros(origB)) { - var alen = length(origA); - var blen = length(origB); - - var len = alen >= blen - ? alen - : blen; - - return function (a) { - return repeatStr('0', len - length(a)); - }; - } - return false; +function isValidNumber(n) { + return isNumber(n) && !/\./.test(n); } /** - * Get the string length of `val` + * Expose `fillRange` + * @type {Function} */ -function length(val) { - return val.toString().length; -} +module.exports = fillRange; diff --git a/package.json b/package.json index d1bb53d..ea43e57 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,13 @@ { "name": "fill-range", - "description": "Fill in a range of numbers or letters, optionally passing an increment or multiplier to use.", - "version": "2.2.3", + "description": "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`", + "version": "3.0.0", "homepage": "https://github.com/jonschlinkert/fill-range", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "contributors": [ + "Jon Schlinkert (http://twitter.com/jonschlinkert)", + "Paul Miller (paulmillr.com)" + ], "repository": "jonschlinkert/fill-range", "bugs": { "url": "https://github.com/jonschlinkert/fill-range/issues" @@ -20,31 +24,31 @@ "test": "mocha" }, "dependencies": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.5.4", + "to-regex-range": "^0.2.0" }, "devDependencies": { - "benchmarked": "^0.1.3", - "chalk": "^0.5.1", - "should": "*", - "mocha": "^2.3.4" + "gulp-format-md": "^0.1.10", + "mocha": "^3.0.2" }, "keywords": [ + "array", "alpha", "alphabetical", "bash", "brace", "expand", "expansion", + "fill", "glob", "match", "matches", "matching", "number", "numerical", + "regex", "range", "ranges", "sh" @@ -52,11 +56,26 @@ "verb": { "related": { "list": [ - "micromatch", - "expand-range", "braces", - "is-glob" + "to-regex-range", + "expand-range", + "micromatch" ] - } + }, + "toc": true, + "layout": "default", + "tasks": [ + "readme" + ], + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + }, + "reflinks": [ + "verb", + "verb-generate-readme" + ] } } diff --git a/test/custom-expand.js b/test/custom-expand.js new file mode 100644 index 0000000..be4744b --- /dev/null +++ b/test/custom-expand.js @@ -0,0 +1,39 @@ +'use strict'; + +require('mocha'); +var repeat = require('repeat-string'); +var isNumber = require('is-number'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('custom function for expansions:', function() { + it('should expose the current value as the first param.', function() { + var arr = fill(1, 5, function(val, a, b, step, idx, arr, options) { + return val; + }); + exact(arr, [1, 2, 3, 4, 5]); + }); + + it('should expose the `isNumber` boolean as the second param.', function() { + var arr = fill('a', 'e', function(val, a, b, step, idx, arr, options) { + return !isNumber ? String.fromCharCode(val) : val; + }); + exact(arr, ['a', 'b', 'c', 'd', 'e']); + }); + + it('should expose padding `maxLength` on options', function() { + var arr = fill('01', '05', function(val, a, b, step, idx, arr, options) { + // increase padding by two + return repeat('0', (options.maxLength + 2) - val.length) + val; + }); + exact(arr, ['0001', '0002', '0003', '0004', '0005']); + }); + + it('should expose the index as the fifth param.', function() { + var arr = fill('a', 'e', function(val, a, b, step, idx, arr, options) { + return val + (idx - 1); + }); + exact(arr, ['a0', 'b1', 'c2', 'd3', 'e4']); + }); +}); diff --git a/test/errors.js b/test/errors.js new file mode 100644 index 0000000..cb8ba2a --- /dev/null +++ b/test/errors.js @@ -0,0 +1,43 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var fill = require('..'); + +describe('error handling', function() { + it('should throw when range arguments are invalid and `strict` is true.', function() { + assert.throws(function() { + fill('0a', '0z', {strict: true}); + }, /invalid range arguments: \[ \'0a\', \'0z\' \]/); + + assert.throws(function() { + fill('.', '*', 2, {strict: true}); + }, /invalid range arguments: \[ \'\.\', \'\*\' \]/); + + assert.throws(function() { + fill('!', '$', {strict: true}); + }, /invalid range arguments: \[ \'\!\', \'\$\' \]/); + }); + + it('should throw when args are incompatible.', function() { + assert.throws(function() { + fill('a8', 10, {strict: true}); + }, /invalid range arguments: \[ \'a8\', 10 \]/); + + assert.throws(function() { + fill(1, 'zz', {strict: true}); + }, /invalid range arguments: \[ 1, \'zz\' \]/); + }); + + it('should throw when the step is bad.', function() { + assert.throws(function() { + fill('1', '10', 'z', {strict: true}); + }, /expected options\.step to be a number/); + assert.throws(function() { + fill('a', 'z', 'a', {strict: true}); + }, /expected options\.step to be a number/); + assert.throws(function() { + fill('a', 'z', '0a', {strict: true}); + }, /expected options\.step to be a number/); + }); +}); diff --git a/test/invalid.js b/test/invalid.js new file mode 100644 index 0000000..85623b4 --- /dev/null +++ b/test/invalid.js @@ -0,0 +1,29 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var fill = require('..'); + +describe('invalid ranges', function() { + it('should return an empty array when options.strict is not true', function() { + assert.deepEqual(fill('1.1', '2.1'), []); + assert.deepEqual(fill('1.2', '2'), []); + assert.deepEqual(fill('1.20', '2'), []); + assert.deepEqual(fill('1', '0f'), []); + assert.deepEqual(fill('1', '10', 'ff'), []); + assert.deepEqual(fill('1', '10.f'), []); + assert.deepEqual(fill('1', '10f'), []); + assert.deepEqual(fill('1', '20', '2f'), []); + assert.deepEqual(fill('1', '20', 'f2'), []); + assert.deepEqual(fill('1', '2f', '2'), []); + assert.deepEqual(fill('1', 'ff', '2'), []); + assert.deepEqual(fill('1', 'ff'), []); + }); +}); diff --git a/test/matching.js b/test/matching.js new file mode 100644 index 0000000..e71e972 --- /dev/null +++ b/test/matching.js @@ -0,0 +1,56 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +function toRegex() { + var str = fill.apply(null, arguments); + return new RegExp('^(' + str + ')$'); +} +function isMatch() { + var args = [].slice.call(arguments); + var last = args.pop(); + var re = toRegex.apply(null, args); + // console.log(re) + return re.test(last); +} + +describe('when options.toRegex is used', function() { + it('should create regex ranges for numbers in ascending order', function() { + assert(!isMatch(2, 8, {toRegex: true}, '10')); + assert(isMatch(2, 8, {toRegex: true}, '3')); + assert(isMatch(2, 10, {toRegex: true}, '10')); + assert(isMatch(2, 100, {toRegex: true}, '10')); + assert(!isMatch(2, 100, {toRegex: true}, '101')); + }); + + it('should create regex ranges with positive and negative numbers', function() { + assert(isMatch(-10, 10, {toRegex: true}, '10')); + assert(isMatch(-10, 10, 2, {toRegex: true}, '10')); + }); + + it('should create regex ranges for numbers in descending order', function() { + assert(isMatch(8, 2, {toRegex: true}, '2')); + assert(isMatch(8, 2, {toRegex: true}, '8')); + assert(!isMatch(8, 2, {toRegex: true}, '10')); + }); + + it('should create regex ranges when a step is given', function() { + assert(!isMatch(8, 2, {toRegex: true, step: 2}, '10')); + assert(!isMatch(8, 2, {toRegex: true, step: 2}, '3')); + assert(!isMatch(8, 2, {toRegex: true, step: 2}, '5')); + assert(isMatch(8, 2, {toRegex: true, step: 2}, '8')); + assert(!isMatch(2, 8, {toRegex: true, step: 2}, '10')); + assert(!isMatch(2, 8, {toRegex: true, step: 2}, '3')); + assert(isMatch(2, 8, {toRegex: true, step: 2}, '8')); + }); +}); diff --git a/test/options.js b/test/options.js new file mode 100644 index 0000000..2275df8 --- /dev/null +++ b/test/options.js @@ -0,0 +1,56 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('options', function() { + describe('options.stringify', function() { + it('should cast values to strings', function() { + exact(fill('1', '10', '1', {stringify: true}), ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + exact(fill(2, 10, '2', {stringify: true}), ['2', '4', '6', '8', '10']); + exact(fill(2, 10, 1, {stringify: true}), ['2', '3', '4', '5', '6', '7', '8', '9', '10']); + exact(fill(2, 10, 3, {stringify: true}), ['2', '5', '8']); + }); + }); + + describe('options.toRegex', function() { + it('should create regex ranges for numbers in ascending order', function() { + assert.equal(fill(2, 8, {toRegex: true}), '[2-8]'); + assert.equal(fill(2, 10, {toRegex: true}), '[2-9]|10'); + assert.equal(fill(2, 100, {toRegex: true}), '[2-9]|[1-9][0-9]|100'); + }); + + it('should create regex ranges for alpha chars defined in ascending order', function() { + assert.equal(fill('a', 'b', {toRegex: true}), '[a-b]'); + assert.equal(fill('A', 'b', {toRegex: true}), '[A-b]'); + assert.equal(fill('Z', 'a', {toRegex: true}), '[Z-a]'); + }); + + it('should create regex ranges for alpha chars defined in descending order', function() { + assert.equal(fill('z', 'A', {toRegex: true}), '[A-z]'); + }); + + it('should create regex ranges with positive and negative numbers', function() { + assert.equal(fill(-10, 10, {toRegex: true}), '-[1-9]|-?10|[0-9]'); + assert.equal(fill(-10, 10, 2, {toRegex: true}), '0|2|4|6|8|10|-(10|8|6|4|2)'); + }); + + it('should create regex ranges for numbers in descending order', function() { + assert.equal(fill(8, 2, {toRegex: true}), '[2-8]'); + }); + + it('should create regex ranges when a step is given', function() { + assert.equal(fill(8, 2, {toRegex: true, step: 2}), '8|6|4|2'); + assert.equal(fill(2, 8, {toRegex: true, step: 2}), '2|4|6|8'); + }); + }); +}); diff --git a/test/padding.js b/test/padding.js new file mode 100644 index 0000000..52fe751 --- /dev/null +++ b/test/padding.js @@ -0,0 +1,48 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var util = require('util'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('padding: numbers', function() { + it('should pad incremented numbers:', function() { + exact(fill('1', '3'), ['1', '2', '3']); + exact(fill('01', '03'), ['01', '02', '03']); + exact(fill('01', '3'), ['01', '02', '03']); + exact(fill('1', '03'), ['01', '02', '03']); + exact(fill('0001', '0003'), ['0001', '0002', '0003']); + exact(fill('-10', '00'), ['-10', '-09', '-08', '-07', '-06', '-05', '-04', '-03', '-02', '-01', '000']); + exact(fill('05', '010'), ['005','006','007','008','009','010']); + exact(fill('05', '100'), ['005','006','007','008','009','010','011','012','013','014','015','016','017','018','019','020','021','022','023','024','025','026','027','028','029','030','031','032','033','034','035','036','037','038','039','040','041','042','043','044','045','046','047','048','049','050','051','052','053','054','055','056','057','058','059','060','061','062','063','064','065','066','067','068','069','070','071','072','073','074','075','076','077','078','079','080','081','082','083','084','085','086','087','088','089','090','091','092','093','094','095','096','097','098','099','100']); + }); + + it('should pad decremented numbers:', function() { + exact(fill('03', '01'), ['03', '02', '01']); + exact(fill('3', '01'), ['03', '02', '01']); + exact(fill('003', '1'), ['003', '002', '001']); + exact(fill('003', '001'), ['003', '002', '001']); + exact(fill('3', '001'), ['003', '002', '001']); + exact(fill('03', '001'), ['003', '002', '001']); + }); + + it('should pad stepped numbers', function() { + exact(fill('1', '05', '3'), ['01','04']); + exact(fill('1', '005', '3'), ['001','004']); + exact(fill('00', '1000', '200'), ['0000','0200', '0400', '0600', '0800', '1000']); + exact(fill('0', '01000', '200'), ['00000','00200', '00400', '00600', '00800', '01000']); + exact(fill('001', '5', '3'), ['001','004']); + exact(fill('02', '10', 2), ['02', '04', '06', '08', '10']); + exact(fill('002', '10', 2), ['002', '004', '006', '008', '010']); + exact(fill('002', '010', 2), ['002', '004', '006', '008', '010']); + exact(fill('-04', 4, 2), ['-04', '-02', '000', '002', '004']); + }); +}); diff --git a/test/ranges.js b/test/ranges.js new file mode 100644 index 0000000..1feeead --- /dev/null +++ b/test/ranges.js @@ -0,0 +1,106 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('ranges', function() { + describe('alphabetical', function() { + it('should increment alphabetical ranges', function() { + exact(fill('a'), ['a']); + exact(fill('a', 'a'), ['a']); + exact(fill('a', 'b'), ['a', 'b']); + exact(fill('a', 'e'), ['a', 'b', 'c', 'd', 'e']); + exact(fill('A', 'E'), ['A', 'B', 'C', 'D', 'E']); + }); + + it('should decrement alphabetical ranges', function() { + exact(fill('E', 'A'), ['E', 'D', 'C', 'B', 'A']); + exact(fill('a', 'C'), ['a','`','_','^',']',"\\",'[','Z','Y','X','W','V','U','T','S','R','Q','P','O','N','M','L','K','J','I','H','G','F','E','D','C']); + exact(fill('z', 'm'), ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm']); + }); + }); + + describe('alphanumeric', function() { + it('should increment alphanumeric ranges', function() { + exact(fill('9', 'B'), ['9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B']); + exact(fill('A', '10'), ['A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']); + exact(fill('a', '10'), ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']); + }); + + it('should step alphanumeric ranges', function() { + exact(fill('9', 'B', 3), [ '9', '<', '?', 'B' ]); + }); + + it('should decrement alphanumeric ranges', function() { + exact(fill('C', '9'), ['C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9']); + }); + }); + + describe('ranges: letters', function() { + it('should increment alphabetical ranges', function() { + exact(fill('a'), ['a']); + exact(fill('a', 'a'), ['a']); + exact(fill('a', 'b'), ['a', 'b']); + exact(fill('a', 'e'), ['a', 'b', 'c', 'd', 'e']); + exact(fill('A', 'E'), ['A', 'B', 'C', 'D', 'E']); + }); + + it('should decrement alphabetical ranges', function() { + exact(fill('E', 'A'), ['E', 'D', 'C', 'B', 'A']); + exact(fill('a', 'C'), ['a','`','_','^',']',"\\",'[','Z','Y','X','W','V','U','T','S','R','Q','P','O','N','M','L','K','J','I','H','G','F','E','D','C']); + exact(fill('z', 'm'), ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm']); + }); + }); + + describe('numbers', function() { + it('should increment numerical *string* ranges', function() { + exact(fill('1'), ['1']); + exact(fill('1', '1'), ['1']); + exact(fill('1', '2'), ['1', '2']); + exact(fill('1', '10'), ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + exact(fill('1', '3'), ['1', '2', '3']); + exact(fill('5', '8'), ['5', '6', '7', '8']); + exact(fill(1, '9'), ['1', '2', '3', '4', '5', '6', '7', '8', '9']); + }); + + it('should increment numerical *number* ranges', function() { + exact(fill(1, 3), [1, 2, 3]); + exact(fill(1, 9), [1, 2, 3, 4, 5, 6, 7, 8, 9]); + exact(fill(5, 8), [5, 6, 7, 8]); + }); + + it('should increment numerical ranges that are a combination of number and string', function() { + exact(fill(1, '9'), ['1', '2', '3', '4', '5', '6', '7', '8', '9']); + exact(fill('2', 5), ['2', '3', '4', '5']); + }); + + it('should decrement numerical *string* ranges', function() { + exact(fill('0', '-5'), ['0', '-1', '-2', '-3', '-4', '-5']); + exact(fill('-1', '-5'), ['-1', '-2', '-3', '-4', '-5']); + }); + + it('should decrement numerical *number* ranges', function() { + exact(fill(-10, -1), [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]); + exact(fill(0, -5), [0, -1, -2, -3, -4, -5]); + }); + + it('should handle *string* ranges ranges that are positive and negative:', function() { + exact(fill('9', '-4'), ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']); + exact(fill('-5', '5'), ['-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5']); + }); + + it('should handle *number* ranges ranges that are positive and negative:', function() { + exact(fill(9, -4), [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4]); + exact(fill(-5, 5), [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]); + }); + }); +}); diff --git a/test/special.js b/test/special.js new file mode 100644 index 0000000..fc3f0fc --- /dev/null +++ b/test/special.js @@ -0,0 +1,36 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('special cases', function() { + describe('negative zero', function() { + it('should handle negative zero', function() { + exact(fill('-5', '-0', '-1'), ['-5', '-4', '-3', '-2', '-1', '0']); + exact(fill('1', '-0', 1), ['1', '0']); + exact(fill('1', '-0', 0), ['1', '0']); + exact(fill('1', '-0', '0'), ['1', '0']); + exact(fill('1', '-0', '1'), ['1', '0']); + exact(fill('-0', '-0', '1'), ['0']); + exact(fill('-0', '0', '1'), ['0']); + exact(fill('-0', '5', '1'), ['0', '1', '2', '3', '4', '5']); + exact(fill(-0, 5), [0, 1, 2, 3, 4, 5]); + exact(fill(5, -0, 5), [5, 0]); + exact(fill(5, -0, 2), [5, 3, 1]); + exact(fill(0, 5, 2), [0, 2, 4]); + }); + + it('should adjust padding for negative numbers:', function() { + exact(fill('-01', '5'), ['-01','000','001','002','003','004','005']); + }); + }); +}); diff --git a/test/steps.js b/test/steps.js new file mode 100644 index 0000000..88b5de5 --- /dev/null +++ b/test/steps.js @@ -0,0 +1,85 @@ +/*! + * fill-range + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +var assert = require('assert'); +var exact = require('./support/exact'); +var fill = require('..'); + +describe('steps', function() { + describe('steps: numbers', function() { + it('should increment ranges using the given step', function() { + exact(fill('1', '10', '1'), ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); + exact(fill('1', '10', '2'), ['1', '3', '5', '7', '9']); + exact(fill('0', '1000', '200'), ['0','200', '400', '600', '800', '1000']); + exact(fill('1', '10', 2), ['1', '3', '5', '7', '9']); + exact(fill('1', '20', '2'), ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); + exact(fill('1', '20', '20'), ['1']); + exact(fill('10', '1', '2'), ['10', '8', '6', '4', '2']); + exact(fill('10', '1', '-2'), ['10', '8', '6', '4', '2']); + exact(fill('10', '1', '2'), ['10', '8', '6', '4', '2']); + exact(fill(2, 10, '2'), ['2', '4', '6', '8', '10']); + exact(fill(2, 10, 1), [2, 3, 4, 5, 6, 7, 8, 9, 10]); + exact(fill(2, 10, 2), [2, 4, 6, 8, 10]); + exact(fill(2, 10, 3), [2, 5, 8]); + exact(fill(0, 5, 2), [0, 2, 4]); + exact(fill(5, 0, 2), [5, 3, 1]); + exact(fill(1, 5, 2), [1, 3, 5]); + exact(fill(2, '10', '2'), ['2', '4', '6', '8', '10']); + exact(fill(2, '10', 1), ['2', '3', '4', '5', '6', '7', '8', '9', '10']); + exact(fill(2, '10', '2'), ['2', '4', '6', '8', '10']); + exact(fill('2', 10, '3'), ['2', '5', '8']); + }); + + it('should fill in negative ranges using the given step (strings)', function() { + exact(fill('0', '-10', '-2'), ['0', '-2', '-4', '-6', '-8', '-10']); + exact(fill('-0', '-10', '-2'), ['0', '-2', '-4', '-6', '-8', '-10']); + exact(fill('-1', '-10', '-2'), ['-1', '-3', '-5', '-7', '-9']); + exact(fill('-1', '-10', '2'), ['-1', '-3', '-5', '-7', '-9']); + exact(fill('1', '10', '2'), ['1', '3', '5', '7', '9']); + exact(fill('1', '20', '2'), ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); + exact(fill('1', '20', '20'), ['1']); + exact(fill('10', '1', '-2'), ['10', '8', '6', '4', '2']); + exact(fill('-10', '0', '2'), ['-10', '-8', '-6', '-4', '-2', '0']); + exact(fill('-10', '-0', '2'), ['-10', '-8', '-6', '-4', '-2', '0']); + exact(fill('-0', '-10', '0'), ['0', '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); + exact(fill('0', '-10', '-0'), ['0', '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); + }); + + it('should fill in negative ranges using the given step (numbers)', function() { + exact(fill(-10, 0, 2), [-10, -8, -6, -4, -2, 0]); + exact(fill(-10, -2, 2), [-10, -8, -6, -4, -2]); + exact(fill(-2, -10, 1), [-2, -3, -4, -5, -6, -7, -8, -9, -10]); + exact(fill(0, -10, 2), [0, -2, -4, -6, -8, -10]); + exact(fill(-2, -10, 2), [-2, -4, -6, -8, -10]); + exact(fill(-2, -10, 3), [-2, -5, -8]); + exact(fill(-9, 9, 3), [-9, -6, -3, 0, 3, 6, 9]); + }); + + it('should fill in negative ranges when negative zero is passed', function() { + exact(fill(-10, -0, 2), [-10, -8, -6, -4, -2, 0]); + exact(fill(-0, -10, 2), [0, -2, -4, -6, -8, -10]); + }); + }); + + describe('steps: letters', function() { + it('should use increments with alphabetical ranges', function() { + exact(fill('a', 'e', 2), ['a','c', 'e']); + exact(fill('E', 'A', 2), ['E', 'C', 'A']); + }); + }); + + describe('options: step', function() { + it('should use the step defined on the options:', function() { + var opts = { step: 2 }; + exact(fill('a', 'e', opts), ['a','c', 'e']); + exact(fill('E', 'A', opts), ['E', 'C', 'A']); + }); + }); +}); diff --git a/test/support/exact.js b/test/support/exact.js new file mode 100644 index 0000000..3636ea4 --- /dev/null +++ b/test/support/exact.js @@ -0,0 +1,13 @@ +'use strict'; + +var util = require('util'); +var assert = require('assert'); + +module.exports = function exact(a, b) { + assert(Array.isArray(a)); + assert(Array.isArray(b)); + + var aString = util.inspect(a, null, 10); + var bString = util.inspect(b, null, 10); + assert.equal(aString, bString, aString + ' !== ' + bString); +}; diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 4a4fe26..0000000 --- a/test/test.js +++ /dev/null @@ -1,391 +0,0 @@ -/*! - * fill-range - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -require('should'); -var range = require('..'); - -describe('error handling', function () { - it('should throw when the first arg is not a string.', function () { - (function() { - range(); - }).should.throw('fill-range expects the first and second args to be strings.'); - }); - - it('should throw when range arguments are invalid and `silent` is false.', function () { - (function () { - range('0a', '0z', {silent: false}); - }).should.throw('fill-range: invalid range arguments.'); - (function () { - range('!', '$', {silent: false}); - }).should.throw('fill-range: invalid range arguments.'); - }); - - it('should throw when args are incompatible.', function () { - (function() { - range('a', 10, {silent: false}); - }).should.throw('fill-range: first range argument is incompatible with second.'); - - (function() { - range(1, 'z', {silent: false}); - }).should.throw('fill-range: first range argument is incompatible with second.'); - }); - - it('should throw when the step is bad.', function () { - (function () { - range('1', '10', 'z', {silent: false}); - }).should.throw('fill-range: invalid step.'); - (function () { - range('a', 'z', 'a', {silent: false}); - }).should.throw('fill-range: invalid step.'); - (function () { - range('a', 'z', '0a', {silent: false}); - }).should.throw('fill-range: invalid step.'); - }); -}); - -describe('ranges: numbers', function () { - it('should increment numerical ranges', function () { - range('1', '10').should.eql(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - range('1', '3').should.eql(['1', '2', '3']); - range('5', '8').should.eql(['5', '6', '7', '8']); - range(1, '9').should.eql(['1', '2', '3', '4', '5', '6', '7', '8', '9']); - range(1, 3).should.eql(['1', '2', '3']); - range(1, 9).should.eql(['1', '2', '3', '4', '5', '6', '7', '8', '9']); - range(5, 8).should.eql(['5', '6', '7', '8']); - }); - - it('should decrement numerical ranges', function () { - range('0', '-5').should.eql(['0', '-1', '-2', '-3', '-4', '-5']); - range(-10, -1).should.eql(['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']); - range(0, -5).should.eql(['0', '-1', '-2', '-3', '-4', '-5']); - }); - - it('should handle ranges ranges that are positive and negative:', function () { - range(9, -4).should.eql(['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']); - range(-5, 5).should.eql(['-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5']); - }); -}); - -describe('ranges: letters', function () { - it('should increment alphabetical ranges', function () { - range('a', 'e').should.eql(['a', 'b', 'c', 'd', 'e']); - range('A', 'E').should.eql(['A', 'B', 'C', 'D', 'E']); - }); - - it('should decrement alphabetical ranges', function () { - range('a', 'C').should.eql(['a','`','_','^',']',"\\",'[','Z','Y','X','W','V','U','T','S','R','Q','P','O','N','M','L','K','J','I','H','G','F','E','D','C']); - range('E', 'A').should.eql(['E', 'D', 'C', 'B', 'A']); - range('z', 'm').should.eql(['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm']); - }); -}); - -describe('steps: numbers', function () { - it('should increment ranges using the given step', function () { - range('1', '10', '1').should.eql(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); - range('1', '10', '2').should.eql(['1', '3', '5', '7', '9']); - range('0', '1000', '200').should.eql(['0','200', '400', '600', '800', '1000']); - range('1', '10', 2).should.eql(['1', '3', '5', '7', '9']); - range('1', '20', '2').should.eql(['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); - range('1', '20', '20').should.eql(['1']); - range('10', '1', '-2').should.eql(['10', '8', '6', '4', '2']); - range('10', '1', '2').should.eql(['10', '8', '6', '4', '2']); - range(2, 10, '2').should.eql(['2', '4', '6', '8', '10']); - range(2, 10, 1).should.eql(['2', '3', '4', '5', '6', '7', '8', '9', '10']); - range(2, 10, 2).should.eql(['2', '4', '6', '8', '10']); - range(2, 10, 3).should.eql(['2', '5', '8']); - range(2, '10', '2').should.eql(['2', '4', '6', '8', '10']); - range(2, '10', 1).should.eql(['2', '3', '4', '5', '6', '7', '8', '9', '10']); - range(2, '10', '2').should.eql(['2', '4', '6', '8', '10']); - range('2', 10, '3').should.eql(['2', '5', '8']); - }); - - it('should fill in negative ranges using the given step', function () { - range('-1', '-10', '-2').should.eql(['-1', '-3', '-5', '-7', '-9']); - range('-1', '-10', '2').should.eql(['-1', '-3', '-5', '-7', '-9']); - range('1', '10', '2').should.eql(['1', '3', '5', '7', '9']); - range('1', '20', '2').should.eql(['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']); - range('1', '20', '20').should.eql(['1']); - range('10', '1', '-2').should.eql(['10', '8', '6', '4', '2']); - range(-10, -2, 2).should.eql(['-10', '-8', '-6', '-4', '-2']); - range(-2, -10, 1).should.eql(['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']); - range(-2, -10, 2).should.eql(['-2', '-4', '-6', '-8', '-10']); - range(-2, -10, 3).should.eql(['-2', '-5', '-8']); - range(-9, 9, 3).should.eql(['-9', '-6', '-3', '0', '3', '6', '9']); - }); -}); - -describe('steps: letters', function () { - it('should use increments with alphabetical ranges', function () { - range('a', 'e', 2).should.eql(['a','c', 'e']); - range('E', 'A', 2).should.eql(['E', 'C', 'A']); - }); -}); - -describe('options: step', function () { - it('should use the step defined on the options:', function () { - var opts = { step: 2 }; - range('a', 'e', opts).should.eql(['a','c', 'e']); - range('E', 'A', opts).should.eql(['E', 'C', 'A']); - }); -}); - -describe('padding: numbers', function () { - it('should pad numbers:', function () { - range('01', '03').should.eql(['01', '02', '03']); - range('0001', '0003').should.eql(['0001', '0002', '0003']); - range('-10', '00').should.eql(['-10', '-09', '-08', '-07', '-06', '-05', '-04', '-03', '-02', '-01', '000']); - range('05', '100').should.eql(['005','006','007','008','009','010','011','012','013','014','015','016','017','018','019','020','021','022','023','024','025','026','027','028','029','030','031','032','033','034','035','036','037','038','039','040','041','042','043','044','045','046','047','048','049','050','051','052','053','054','055','056','057','058','059','060','061','062','063','064','065','066','067','068','069','070','071','072','073','074','075','076','077','078','079','080','081','082','083','084','085','086','087','088','089','090','091','092','093','094','095','096','097','098','099','100']); - }); - - it('should pad numbers when a step is passed:', function () { - range('1', '05', '3').should.eql(['01','04']); - range('1', '005', '3').should.eql(['001','004']); - range('00', '1000', '200').should.eql(['0000','0200', '0400', '0600', '0800', '1000']); - range('0', '01000', '200').should.eql(['00000','00200', '00400', '00600', '00800', '01000']); - range('001', '5', '3').should.eql(['001','004']); - range('02', '10', 2).should.eql(['02', '04', '06', '08', '10']); - range('002', '10', 2).should.eql(['002', '004', '006', '008', '010']); - range('002', '010', 2).should.eql(['002', '004', '006', '008', '010']); - range('-04', 4, 2).should.eql(['-04', '-02', '000', '002', '004']); - }); - - it('should return `null` for invalid ranges:', function () { - (range('1.1', '2.1') == null).should.be.true; - (range('1.2', '2') == null).should.be.true; - (range('1.20', '2') == null).should.be.true; - (range('1', '0f') == null).should.be.true; - (range('1', '10', 'ff') == null).should.be.true; - (range('1', '10.f') == null).should.be.true; - (range('1', '10f') == null).should.be.true; - (range('1', '20', '2f') == null).should.be.true; - (range('1', '20', 'f2') == null).should.be.true; - (range('1', '2f', '2') == null).should.be.true; - (range('1', 'ff', '2') == null).should.be.true; - (range('1', 'ff') == null).should.be.true; - (range('1', 'f') == null).should.be.true; - (range('f', '1') == null).should.be.true; - }); -}); - -describe('special cases', function () { - it('should convert `-0` to `0`:', function () { - range('-5', '-0', '-1').should.eql(['-5', '-4', '-3', '-2', '-1', '0']); - range('1', '-0', '2').should.eql(['1']); - range('1', '-0', '0').should.eql(['1', '0']); - }); - - it('should adjust padding for negative numbers:', function () { - range('-01', '5').should.eql(['-01','000','001','002','003','004','005']); - }); -}); - -describe('special characters:', function () { - it('should use the step defined on the options:', function () { - var opts = { step: '~' }; - range('a', 'e', opts).should.eql(['[a-e]']); - range('E', 'A', opts).should.eql(['(E|D|C|B|A)']); - }); - - describe('repeat:', function () { - it('should repeat the first arg `n` times:', function () { - range('a', 3, '+').should.eql(['a', 'a', 'a']); - range('abc', 2, '+').should.eql(['abc', 'abc']); - }); - }); - - describe('join:', function () { - it('should join values when `>` is passed:', function () { - range('a', 'e', '>').should.eql(['abcde']); - range('A', 'E', '>').should.eql(['ABCDE']); - range('E', 'A', '>').should.eql(['EDCBA']); - range('5', '8', '>').should.eql(['5678']); - }); - - it('should use steps with joined values:', function () { - range('A', 'Z', '>5').should.eql(['AFKPUZ']); - range('2', '20', '2>').should.eql(['2468101214161820']); - range('2', '20', '>2').should.eql(['2468101214161820']); - }); - }); - - describe('randomize:', function () { - it('should randomize using the first two args when `?` is passed:', function () { - range('A0', 5, '?').should.match(/[\w\d]{5}/); - range('A0', 5, '?').should.not.match(/[\w\d]{6}/); - range('A', 5, '?').should.not.match(/\d{5}/); - range('*', 5, '?').should.match(/.{5}/); - range('aA0', 10, '?').should.match(/[^\W]{10}/); - }); - }); - - describe('regex string:', function () { - it('should respect padding when special chars are used:', function () { - range('05', '100', '10').should.eql(['005','015','025','035','045','055','065','075','085','095']); - range('05', '100', '10|').should.eql(['(005|015|025|035|045|055|065|075|085|095)']); - range('05', '100', '|10').should.eql(['(005|015|025|035|045|055|065|075|085|095)']); - range('05', '100', '10~').should.eql(['(005|015|025|035|045|055|065|075|085|095)']); - }); - - it('should create a regex logical `or`:', function () { - range('c', 'a', '|').should.eql(['(c|b|a)']); - range('z', 'a', '|5').should.eql(['(z|u|p|k|f|a)']); - range('a', 'e', '2|').should.eql(['(a|c|e)']); - range('a', 'e', '|2').should.eql(['(a|c|e)']); - range('a', 'z', '|5').should.eql(['(a|f|k|p|u|z)']); - range('a', 'C', '|').should.eql(['(a|_|Z|Y|X|W|V|U|T|S|R|Q|P|O|N|M|L|K|J|I|H|G|F|E|D|C)']); - range('a', 'C', '~').should.eql(['(a|_|Z|Y|X|W|V|U|T|S|R|Q|P|O|N|M|L|K|J|I|H|G|F|E|D|C)']); - }); - - it('should create a regex character class (range):', function () { - range('a', 'c', '|').should.eql(['[a-c]']); - range('1', '3', '|').should.eql(['[1-3]']); - }); - - it('should create a regex character class when `optimize` is true:', function () { - range('a', 'c', {optimize: true}).should.eql(['[a-c]']); - range('1', '3', {optimize: true}).should.eql(['[1-3]']); - }); - - it('should not create a regex character class when `optimize` is false:', function () { - range('a', 'c', {optimize: false}).should.eql(['a', 'b', 'c']); - range('1', '3', {optimize: false}).should.eql(['1', '2', '3']); - }); - - it('should not create a regex character class for negative numbers:', function () { - range('-1', '2').should.eql(['-1', '0', '1', '2']); - range('-1', '2', '~').should.eql(['-1', '0', '1', '2']); - range('0', '-2').should.eql(['0', '-1', '-2']); - range('0', '-2', '~').should.eql(['0', '-1', '-2']); - }); - - it('should not wrap a single value:', function () { - range(1, 1, '~').should.eql(['1']); - range('e', 'e', '~').should.eql(['e']); - }); - - it('when a step is zero it should be coerced to a one:', function () { - range('a', 'e', '0').should.eql(['a', 'b', 'c', 'd', 'e']); - range('a', 'e', '0|').should.eql(['[a-e]']); - range('a', 'e', '~0').should.eql(['[a-e]']); - range('a', 'e', '|0').should.eql(['[a-e]']); - }); - }); -}); - -describe('syntax detection:', function () { - describe('should determine the correct syntax to avoid creating out-of-order ranges:', function () { - it('should create valid character classes for numbers:', function () { - range(1, 3, '~').should.eql(['[1-3]']); - range(1, 3, '~1').should.eql(['[1-3]']); - range(1, 3, '|').should.eql(['[1-3]']); - range(1, 3, '|1').should.eql(['[1-3]']); - - range('1', '3', '|').should.eql(['[1-3]']); - range('1', '3', '|1').should.eql(['[1-3]']); - range('1', '3', '~').should.eql(['[1-3]']); - range('1', '3', '~1').should.eql(['[1-3]']); - - range('0', '9', '|').should.eql(['[0-9]']); - range('0', '9', '~').should.eql(['[0-9]']); - }); - - it('should create valid logical `or`s for descending numbers:', function () { - range('5', '1', '|').should.eql(['(5|4|3|2|1)']); - range('5', '1', '~').should.eql(['(5|4|3|2|1)']); - range('3', '1', '~1').should.eql(['(3|2|1)']); - range('3', '1', '~|').should.eql(['(3|2|1)']); - }); - - it('should create valid character classes for letters:', function () { - range('a', 'c', '~').should.eql(['[a-c]']); - range('a', 'c', '|').should.eql(['[a-c]']); - range('a', 'z', '~').should.eql(['[a-z]']); - range('a', 'z', '|').should.eql(['[a-z]']); - }); - - it('should create valid logical `or`s for numbers:', function () { - range('1', '10', '~').should.eql(['(1|2|3|4|5|6|7|8|9|10)']); - range('10', '11', '~').should.eql(['(10|11)']); - range('10', '11', '|').should.eql(['(10|11)']); - range('11', '10', '~').should.eql(['(11|10)']); - range('11', '10', '|').should.eql(['(11|10)']); - range(10, 20, '|').should.eql(['(10|11|12|13|14|15|16|17|18|19|20)']); - range(10, 20, '~').should.eql(['(10|11|12|13|14|15|16|17|18|19|20)']); - range(9, 9, '|').should.eql(['9']); - }); - - it('should create valid logical `or`s for letters:', function () { - range('c', 'a', '|').should.eql(['(c|b|a)']); - range('c', 'a', '~').should.eql(['(c|b|a)']); - - range('a', 'z', '5~').should.eql(['(a|f|k|p|u|z)']); - range('a', 'z', '5~').should.eql(['(a|f|k|p|u|z)']); - - range('z', 'a', '5|').should.eql(['(z|u|p|k|f|a)']); - range('z', 'a', '5~').should.eql(['(z|u|p|k|f|a)']); - - range('a', 'C', '|').should.eql(['(a|_|Z|Y|X|W|V|U|T|S|R|Q|P|O|N|M|L|K|J|I|H|G|F|E|D|C)']); - range('a', 'C', '~').should.eql(['(a|_|Z|Y|X|W|V|U|T|S|R|Q|P|O|N|M|L|K|J|I|H|G|F|E|D|C)']); - }); - }); -}); - -describe('prefixes:', function () { - describe('logical or:', function () { - it('should add a prefix to a logical or when passed on the options:', function () { - range('c', 'a', '|', {regexPrefix: '?:'}).should.eql(['(?:c|b|a)']); - range('c', 'a', '~', {regexPrefix: '?:'}).should.eql(['(?:c|b|a)']); - range('1', '10', '|', {regexPrefix: '?!'}).should.eql(['(?!1|2|3|4|5|6|7|8|9|10)']); - }); - }); - describe('character classes:', function () { - it('should add a negation prefix to a character class:', function () { - range('a', 'c', '~', {regexPrefix: '^'}).should.eql(['[^a-c]']); - range('a', 'c', '~', {regexPrefix: '^'}).should.eql(['[^a-c]']); - range('a', 'c', '|', {regexPrefix: '^'}).should.eql(['[^a-c]']); - }); - it('should not add the wrong prefix to a character class:', function () { - range('a', 'c', '~', {regexPrefix: '?!'}).should.eql(['[a-c]']); - range('a', 'c', '~', {regexPrefix: '?:'}).should.eql(['[a-c]']); - range('a', 'c', '|', {regexPrefix: 'foo'}).should.eql(['[a-c]']); - }); - }); -}); - -describe('custom function for expansions:', function () { - it('should expose the current value as the first param.', function () { - range(1, 5, function (val, isNumber, padding, i) { - return val; - }).should.eql([1, 2, 3, 4, 5]); - }); - - it('should expose the `isNumber` boolean as the second param.', function () { - range('a', 'e', function (val, isNumber, padding, i) { - if (!isNumber) { - return String.fromCharCode(val); - } - return val; - }).should.eql(['a', 'b', 'c', 'd', 'e']); - }); - - it('should expose padding as the third param.', function () { - range('01', '05', function (val, isNumber, padding, i) { - return padding + padding + val; - }).should.eql(['001', '002', '003', '004', '005']); - }); - - it('should expose the index as the fourth param.', function () { - range('a', 'e', function (val, isNumber, padding, i) { - if (!isNumber) { - return String.fromCharCode(val) + i; - } - return val; - }).should.eql(['a0', 'b1', 'c2', 'd3', 'e4']); - }); -});