diff --git a/CHANGELOG.md b/CHANGELOG.md index 730677d..e9bb07e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## UNRELEASED +- Adds a new `wordList` option. + ## 2.0.1 (2024-01-25) - Fixed return type of `generate` so that it is consistent with the possibility of returning either `string` or `string[]` diff --git a/README.md b/README.md index e44443f..be4e70e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,32 @@ # random-words -## Generate one or more common English words +## Generate one or more common English words (by default) `random-words` generates random words for use as sample text. We use it to generate random blog posts when testing [Apostrophe](http://apostrophecms.org). -Cryptographic-quality randomness is NOT the goal, as speed matters for generating sample text and security does not. As such, `Math.random()` is used in most cases. +Cryptographic-quality randomness is NOT the goal, as speed matters for generating sample text and security does not. +As such, `Math.random()` is used in most cases. -The `seed` option can be used with the `generate` function for situations that require deterministic output. When given the same `seed` with the same input, `generate()` will yield deterministic results, in regards to both actual word selection and the number of words returned (when using `min` and `max`). The underlying implementation of this option utilizes the [seedrandom](https://www.npmjs.com/package/seedrandom) package as a replacement for `Math.random()`. +The `seed` option can be used with the `generate` function for situations that require deterministic output. +When given the same `seed` with the same input, `generate()` will yield deterministic results, +in regards to both actual word selection and the number of words returned (when using `min` and `max`). +The underlying implementation of this option utilizes the [seedrandom](https://www.npmjs.com/package/seedrandom) package as a replacement for `Math.random()`. The `count` function can be used to calculate the total number of words in the word list that meet the specified minimum and maximum length criteria. -Installation: +A default list of common English words is embedded in (and exported as `wordList` from) the package. +But both `generate` and `count` functions can be supplied a custom list of words +by passing a string array to the `wordList` option. - npm install random-words +### Installation -Examples: +``` +npm install random-words +``` -```js +### Examples + +```javascript import { generate, count } from "random-words"; console.log(generate()); @@ -25,6 +35,9 @@ console.log(generate()); console.log(generate(5)); //output: ['army', 'beautiful', 'became', 'if', 'actually'] +console.log(generate({ wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] })); +//output: 'hello world' + console.log(generate({ minLength: 2 })); //output: 'hello' @@ -111,4 +124,9 @@ console.log(count({ maxLength: 7 })); console.log(count({ minLength: 5, maxLength: 7 })); //output: 1015 -``` \ No newline at end of file +console.log(count({ wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] })); +//output: 5 + +console.log(count({ minLength: 8, wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] })); +//output: 4 +``` diff --git a/index.d.ts b/index.d.ts index d3a6906..ecb42a8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -8,6 +8,7 @@ declare type GenerateOptions = { separator?: string; formatter?: (word: string, index: number) => string; seed?: string; + wordList?: string[]; }; declare type JoinedWordsOptions = GenerateOptions & { join: string }; @@ -16,7 +17,7 @@ declare function generate(count?: number): string | string[]; declare function generate(options: GenerateOptions): string | string[]; declare function generate(options: JoinedWordsOptions): string; -declare const wordsList: string[]; +declare const wordList: string[]; declare type CountOptions = { minLength?: number; @@ -25,4 +26,4 @@ declare type CountOptions = { declare function count(options?: CountOptions): number; -export { generate, count, wordsList }; +export { generate, count, wordList }; diff --git a/index.js b/index.js index f63b43e..4fbb302 100644 --- a/index.js +++ b/index.js @@ -1956,29 +1956,51 @@ export const wordList = [ "zulu", ]; -const shortestWordSize = wordList.reduce((shortestWord, currentWord) => - currentWord.length < shortestWord.length ? currentWord : shortestWord -).length; +function shortestWordSize(wordList) { + return wordList.reduce((shortestWord, currentWord) => + currentWord.length < shortestWord.length ? currentWord : shortestWord + ).length; +} + +function longestWordSize(wordList) { + return wordList.reduce((longestWord, currentWord) => + currentWord.length > longestWord.length ? currentWord : longestWord + ).length; +} -const longestWordSize = wordList.reduce((longestWord, currentWord) => - currentWord.length > longestWord.length ? currentWord : longestWord -).length; +// custom word lists are filtered and validated against emptiness; +// otherwise the default list is returned +function validateWordList(list) { + if (!Array.isArray(list) || list.length === 0) { + return wordList; + } + const cleanedList = list.filter(val => typeof val === "string") + return cleanedList.length > 0 ? cleanedList : wordList; +} export function generate(options) { // initalize random number generator for words if options.seed is provided const random = options?.seed ? new seedrandom(options.seed) : null; + // if `options` is just a number, set `exactly` in options to return that many words + if (typeof options === "number") { + options = { exactly: options }; + } + const { minLength, maxLength, ...rest } = options || {}; + const list = validateWordList(rest?.wordList); + delete rest.wordList; + function word() { let min = - typeof minLength !== "number" - ? shortestWordSize - : limitWordSize(minLength); + typeof minLength !== "number" ? + shortestWordSize(list) : + limitWordSize(minLength); const max = typeof maxLength !== "number" - ? longestWordSize + ? longestWordSize(list) : limitWordSize(maxLength); if (min > max) min = max; @@ -1993,13 +2015,13 @@ export function generate(options) { } function generateRandomWord() { - return wordList[randInt(wordList.length)]; + return list[randInt(list.length)]; } // limits the size of words to the minimum and maximum possible function limitWordSize(wordSize) { - if (wordSize < shortestWordSize) wordSize = shortestWordSize; - if (wordSize > longestWordSize) wordSize = longestWordSize; + if (wordSize < shortestWordSize(list)) wordSize = shortestWordSize(list); + if (wordSize > longestWordSize(list)) wordSize = longestWordSize(list); return wordSize; } @@ -2009,15 +2031,13 @@ export function generate(options) { return Math.floor(r * lessThan); } - // No arguments = generate one word + // No argument = Generates one word as-is if (options === undefined) { return word(); } - // Just a number = return that many words - if (typeof options === "number") { - options = { exactly: options }; - } else if (Object.keys(rest).length === 0) { + // if `options` only contains minLength, maxLength, or wordList, generate one word as-is + if (Object.keys(rest).length === 0) { return word(); } @@ -2034,7 +2054,7 @@ export function generate(options) { //not a function = returns the raw word if (typeof options.formatter !== "function") { - options.formatter = (word) => word; + options.formatter = word => word; } //not a string = separator is a space @@ -2043,7 +2063,7 @@ export function generate(options) { } const total = options.min + randInt(options.max + 1 - options.min); - let results = []; + const results = []; let token = ""; let relativeIndex = 0; @@ -2060,25 +2080,28 @@ export function generate(options) { relativeIndex = 0; } } + if (typeof options.join === "string") { - results = results.join(options.join); + return results.join(options.join); } return results; } export function count(options) { - let { minLength, maxLength } = options || {}; + let { minLength, maxLength, ...rest } = options || {}; + + const list = validateWordList(rest?.wordList); if (typeof minLength !== "number") { - minLength = shortestWordSize; + minLength = shortestWordSize(list); } if (typeof maxLength !== "number") { - maxLength = longestWordSize; + maxLength = longestWordSize(list); } - return wordList.filter( + return list.filter( (word) => word.length >= minLength && word.length <= maxLength ).length; } diff --git a/package.json b/package.json index bf1dffb..8f5087e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "random-words", - "version": "2.0.1", - "description": "Generate one or more common English words", + "version": "2.0.2", + "description": "Generate one or more common English words (by default)", "main": "index.js", "types": "index.d.ts", "type": "module", @@ -27,6 +27,6 @@ "seedrandom": "^3.0.5" }, "devDependencies": { - "mocha": "^10.2.0" + "mocha": "^10.3.0" } -} \ No newline at end of file +} diff --git a/tests/test.js b/tests/test.js index b66490d..f70c2da 100644 --- a/tests/test.js +++ b/tests/test.js @@ -1,295 +1,410 @@ -import assert from "assert"; -import { generate, count } from "../index.js"; -import { wordList } from "../index.js"; +import assert from "node:assert"; +import { generate, count, wordList } from "../index.js"; -const longestWordSize = wordList.reduce((longestWord, currentWord) => - currentWord.length > longestWord.length ? currentWord : longestWord -).length; +const customWordList = [ + "test", + "testTest", + "testTestTest", + "testTestTestTest", + "testTestTestTestTest", + "testTestTestTestTestTest", +]; -describe("random-words : generate", function () { - it("should return one word when called with no arguments", function () { - const word = generate(); - assert.ok(typeof word === "string", "word is a string"); - assert.ok(word.length, "word is not empty"); - assert.ok(word.indexOf(" ") === -1, "word does not contain spaces"); - }); - it("should return 5 words when called with the number 5", function () { - const words = generate(5); - assert.ok(words.length === 5, "contains 5 elements"); - }); - it("should return between 5 and 10 words when called with min: 5 and max: 10", function () { - const words = generate({ min: 5, max: 10 }); - assert.ok(words.length >= 5 && words.length <= 10); - }); - it("returns result of variable length when called with min: 5 and max: 10", function () { - const lengths = {}; - for (let i = 0; i < 100; i++) { - const words = generate({ min: 5, max: 10 }); - lengths[words.length] = true; - } - assert.ok(Object.keys(lengths).length > 1, "result varies in length"); - }); - it("should return 5 space separated words when join is used with exactly: 5", function () { - let phrase = generate({ exactly: 5, join: " " }); - assert.ok(typeof phrase === "string", "result is a string"); - assert.ok(phrase.match(/\S/), "result contains text, not just spaces"); - phrase = phrase.replace(/[\S]/g, ""); - assert.ok( - phrase.length === 4, - "result contains 4 spaces joining the 5 words" - ); - }); - it("should return 5 concatenated words when join is used with an empty string and exactly: 5", function () { - const phrase = generate({ exactly: 5, join: "" }); - assert.ok(typeof phrase === "string", "result is a string"); - assert.ok(phrase.match(/\w/), "result contains text, no spaces"); - }); - it("should return 5 words when called with exactly: 5 and join: false", function () { - const words = generate({ exactly: 5, join: false }); - assert.ok(words.length === 5, "contains 5 elements"); - }); - it("should return 5 words when called with exactly: 5 and join: null", function () { - const words = generate({ exactly: 5, join: null }); - assert.ok(words.length === 5, "contains 5 elements"); - }); - it("should return one word with a minimum of 8 letters", function () { - const minWordSize = 8; - const word = generate({ minLength: minWordSize }); - - assert.ok(word.length >= minWordSize, "result is less than 8 letters"); - }); - it("should return one word with a maximum of 5 letters", function () { - const maxWordSize = 5; - const word = generate({ maxLength: maxWordSize }); - - assert.ok(word.length <= maxWordSize, "result exceeded 5 letters"); - }); - it("should return one word with the length between 3 and 5 ", function () { - const minLengthSize = 3; - const maxLengthSize = 5; - const word = generate({ - minLength: minLengthSize, - maxLength: maxLengthSize, - }); - - assert.ok( - word.length >= minLengthSize && word.length <= maxLengthSize, - "result is not between the limit of 3 and 5" - ); - }); - it("should only return words with a minimum of 8 letters", function () { - const minWordSize = 8; - const words = generate({ exactly: 10000, minLength: minWordSize }); - words.forEach((word) => { - assert.ok(word.length >= minWordSize, "result is less than 8 letters"); - }); - }); - it("should only return words with a maximum of 5 letters", function () { - const maxWordSize = 5; - const words = generate({ exactly: 10000, maxLength: maxWordSize }); - words.forEach((word) => { - assert.ok(word.length <= maxWordSize, "result exceeded 5 letters"); - }); - }); - it("should only return words with the length between 3 and 5", function () { - const minLengthSize = 3; - const maxLengthSize = 5; - const words = generate({ - exactly: 10000, - minLength: minLengthSize, - maxLength: maxLengthSize, - }); - words.forEach((word) => { - assert.ok( - word.length >= minLengthSize && word.length <= maxLengthSize, - "result is not between the limit of 3 and 5" - ); - }); - }); - it("should only return words with length = 5", function () { - const wordSize = 5; - const words = generate({ - exactly: 10000, - minLength: wordSize, - maxLength: wordSize, - }); - words.forEach((word) => { - assert.ok(word.length === wordSize, "word length is different from 5"); - }); - }); - it("maxLength larger than the longest word should not result in an endless loop", function () { - const wordSize = 100000; - const words = generate({ - exactly: 1000, - maxLength: wordSize, - }); - words.forEach((word) => { - assert.ok(word.length <= longestWordSize); - }); - }); - it("minLength larger than the longest word should not result in an endless loop", function () { - const wordSize = 100000; - const words = generate({ - exactly: 1000, - minLength: wordSize, - }); - words.forEach((word) => { - assert.ok(word.length <= longestWordSize); - }); - }); - it("must return a word even without passing a number to minLength and maxLength", function () { - const word1 = generate({ minLength: undefined, maxLength: false }); - const word2 = generate({ minLength: "string", maxLength: null }); - assert.ok( - typeof word1 === "string" && typeof word2 === "string", - "result is not a string" - ); - }); - it("should return 5 space separated words for each string if wordsPerString is set to 5 and exactly > 1", function () { - const words = generate({ exactly: 10, wordsPerString: 5 }); - words.forEach((string) => { - const stringSplitted = string.split(" "); - assert.ok( - stringSplitted.length === 5, - "the i-th string contains 5 words" - ); - }); - }); - it("should reuturn 5 words separated by a separator for each string if wordsPerString > 1, separator is defined as a string and exactly > 1", function () { - const separator = "-"; - const words = generate({ exactly: 10, wordsPerString: 5, separator }); - words.forEach((string) => { - const stringSplitted = string.split(separator); - assert.ok(typeof separator === "string", "separator is a string"); - assert.ok( - stringSplitted.length === 5, - "the i-th string contains 5 words" - ); - }); - }); - it("should return styled strings if formatter is defined as a function that returns a string", function () { - const formatter = (word) => word.toUpperCase(); - assert.ok(typeof formatter === "function", "formatter is a function"); - assert.ok( - typeof formatter("test") === "string", - "formatter returns a string" - ); - const words = generate({ exactly: 10, formatter }); - words.forEach((word) => { - assert.ok(word === word.toUpperCase(), "word is formatted"); - }); - }); - it("should return the same words if the same seed is used", function () { - const seed = "seed1"; - const exactly = 20; - const join = " "; +function getLongestWordLengthFromList (list) { + return list.reduce( + (longestWord, currentWord) => + currentWord.length > longestWord.length ? currentWord : longestWord + ).length; +} - const words = generate({ seed, exactly, join }); - const words2 = generate({ seed, exactly, join }); +describe("random-words", () => { + describe("generate()", () => { + describe("with the default word list", () => { + it("should return one word when called with no arguments", () => { + const word = generate(); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + assert.ok(word.indexOf(" ") === -1, "word does not contain spaces"); + }); + it("should return 1 word in array when called with the number 1", () => { + const words = generate(1); + assert.ok(words.length === 1, "contains 1 elements"); + }); + it("should return 5 words when called with the number 5", () => { + const words = generate(5); + assert.ok(words.length === 5, "contains 5 elements"); + }); + it("should return between 5 and 10 words when called with min: 5 and max: 10", () => { + const words = generate({ min: 5, max: 10 }); + assert.ok(words.length >= 5 && words.length <= 10); + }); + it("returns result of variable length when called with min: 5 and max: 10", () => { + const lengths = {}; + for (let i = 0; i < 100; i++) { + const words = generate({ min: 5, max: 10 }); + lengths[words.length] = true; + } + assert.ok(Object.keys(lengths).length > 1, "result varies in length"); + }); + it("should return 5 space separated words when join is used with exactly: 5", () => { + let phrase = generate({ exactly: 5, join: " " }); + assert.ok(typeof phrase === "string", "result is a string"); + assert.ok(phrase.match(/\S/), "result contains text, not just spaces"); + phrase = phrase.replace(/[\S]/g, ""); + assert.ok( + phrase.length === 4, + "result contains 4 spaces joining the 5 words" + ); + }); + it("should return 5 concatenated words when join is used with an empty string and exactly: 5", () => { + const phrase = generate({ exactly: 5, join: "" }); + assert.ok(typeof phrase === "string", "result is a string"); + assert.ok(phrase.match(/\w/), "result contains text, no spaces"); + }); + it("should return 5 words when called with exactly: 5 and join: false", () => { + const words = generate({ exactly: 5, join: false }); + assert.ok(words.length === 5, "contains 5 elements"); + }); + it("should return 5 words when called with exactly: 5 and join: null", () => { + const words = generate({ exactly: 5, join: null }); + assert.ok(words.length === 5, "contains 5 elements"); + }); + it("should return one word with a minimum of 8 letters", () => { + const minWordSize = 8; + const word = generate({ minLength: minWordSize }); - assert.ok(words == words2, "words are the same"); - }); - it("should return the same number of words if the same seed is used", function () { - const seed = "seed1"; - const min = 1; - const max = 10; + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length >= minWordSize, "result is less than 8 letters"); + }); + it("should return one word with a maximum of 5 letters", () => { + const maxWordSize = 5; + const word = generate({ maxLength: maxWordSize }); - const words = generate({ seed, min, max }); - const words2 = generate({ seed, min, max }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length <= maxWordSize, "result exceeded 5 letters"); + }); + it("should return one word with the length between 3 and 5 ", () => { + const minLengthSize = 3; + const maxLengthSize = 5; + const word = generate({ + minLength: minLengthSize, + maxLength: maxLengthSize, + }); - assert.ok(words.length == words2.length, "number of words is the same"); - }); - it("should return different words if no seeds are provided", function () { - const exactly = 20; - const join = " "; - - const words = generate({ exactly, join }); - const words2 = generate({ exactly, join }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok( + word.length >= minLengthSize && word.length <= maxLengthSize, + "result is not between the limit of 3 and 5" + ); + }); + it("should only return words with a minimum of 8 letters", () => { + const minWordSize = 8; + const words = generate({ exactly: 10000, minLength: minWordSize }); + words.forEach((word) => { + assert.ok(word.length >= minWordSize, "result is less than 8 letters"); + }); + }); + it("should only return words with a maximum of 5 letters", () => { + const maxWordSize = 5; + const words = generate({ exactly: 10000, maxLength: maxWordSize }); + words.forEach((word) => { + assert.ok(word.length <= maxWordSize, "result exceeded 5 letters"); + }); + }); + it("should only return words with the length between 3 and 5", () => { + const minLengthSize = 3; + const maxLengthSize = 5; + const words = generate({ + exactly: 10000, + minLength: minLengthSize, + maxLength: maxLengthSize, + }); + words.forEach((word) => { + assert.ok( + word.length >= minLengthSize && word.length <= maxLengthSize, + "result is not between the limit of 3 and 5" + ); + }); + }); + it("should only return words with length = 5", () => { + const wordSize = 5; + const words = generate({ + exactly: 10000, + minLength: wordSize, + maxLength: wordSize, + }); + words.forEach((word) => { + assert.ok(word.length === wordSize, "word length is different from 5"); + }); + }); + it("maxLength larger than the longest word should not result in an endless loop", () => { + const wordSize = 100000; + const words = generate({ + exactly: 1000, + maxLength: wordSize, + }); + words.forEach((word) => { + assert.ok(word.length <= getLongestWordLengthFromList(wordList)); + }); + }); + it("minLength larger than the longest word should not result in an endless loop", () => { + const wordSize = 100000; + const words = generate({ + exactly: 1000, + minLength: wordSize, + }); + words.forEach((word) => { + assert.ok(word.length <= getLongestWordLengthFromList(wordList)); + }); + }); + it("must return a word even without passing a number to minLength and maxLength", () => { + const word1 = generate({ minLength: undefined, maxLength: false }); + const word2 = generate({ minLength: "string", maxLength: null }); + assert.ok( + typeof word1 === "string" && typeof word2 === "string", + "result is not a string" + ); + }); + it("should return 5 space separated words for each string if wordsPerString is set to 5 and exactly > 1", () => { + const words = generate({ exactly: 10, wordsPerString: 5 }); + words.forEach((string) => { + const stringSplitted = string.split(" "); + assert.ok( + stringSplitted.length === 5, + "the i-th string contains 5 words" + ); + }); + }); + it("should return 5 words separated by a separator for each string if wordsPerString > 1, separator is defined as a string and exactly > 1", () => { + const separator = "-"; + const words = generate({ exactly: 10, wordsPerString: 5, separator }); + words.forEach((string) => { + const stringSplitted = string.split(separator); + assert.ok(typeof separator === "string", "separator is a string"); + assert.ok( + stringSplitted.length === 5, + "the i-th string contains 5 words" + ); + }); + }); + it("should return styled strings if formatter is defined as a function that returns a string", () => { + const formatter = (word) => word.toUpperCase(); + assert.ok(typeof formatter === "function", "formatter is a function"); + assert.ok( + typeof formatter("test") === "string", + "formatter returns a string" + ); + const words = generate({ exactly: 10, formatter }); + words.forEach((word) => { + assert.ok(word === word.toUpperCase(), "word is formatted"); + }); + }); + it("should return the same words if the same seed is used", () => { + const seed = "seed1"; + const exactly = 20; + const join = " "; - // with 1952 possible words, at least one word in 20 should be different - assert.ok(words != words2, "words are different"); - }); - it("should return different words if different seeds are used", function () { - const exactly = 20; + const words = generate({ seed, exactly, join }); + const words2 = generate({ seed, exactly, join }); - const words = generate({ seed: "seed1", exactly }); - const words2 = generate({ seed: "seed2", exactly }); + assert.ok(words === words2, "words are the same"); + }); + it("should return the same number of words if the same seed is used", () => { + const seed = "seed1"; + const min = 1; + const max = 10; - // with these seeds, all words should be different - for (let i = 0; i < exactly; i++) { - assert.ok(words[i] != words2[i], "words are different"); - } - }); - it("should return different number of words if different seeds are used", function () { - const min = 1; - const max = 10; + const words = generate({ seed, min, max }); + const words2 = generate({ seed, min, max }); - const words = generate({ seed: "seed1", min, max }); - const words2 = generate({ seed: "seed2", min, max }); + assert.ok(words.length === words2.length, "number of words is the same"); + }); + it("should return different words if no seeds are provided", () => { + const exactly = 20; + const join = " "; - // with these seeds, the number of words should 5 and 3 - assert.ok(words.length != words2.length, "number of words is different"); - }); -}); + const words = generate({ exactly, join }); + const words2 = generate({ exactly, join }); -describe("random-words : count", function () { - it("should return the correct count when no options are provided", function () { - const totalWords = count(); + // with 1952 possible words, at least one word in 20 should be different + assert.ok(words !== words2, "words are different"); + }); + it("should return different words if different seeds are used", () => { + const exactly = 20; - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); - }); - it("should return the correct count when minLength and maxLength options are provided", function () { - const options = { minLength: 5, maxLength: 8 }; - const totalWords = count(options); + const words = generate({ seed: "seed1", exactly }); + const words2 = generate({ seed: "seed2", exactly }); - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); - }); - it("should return the correct count when only minLength option is provided", function () { - const options = { minLength: 8 }; - const totalWords = count(options); + // with these seeds, all words should be different + for (let i = 0; i < exactly; i++) { + assert.ok(words[i] !== words2[i], "words are different"); + } + }); + it("should return different number of words if different seeds are used", () => { + const min = 1; + const max = 10; - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); - }); - it("should return 0 when no words satisfy the length criteria", function () { - const options = { minLength: 30, maxLength: 35 }; - const totalWords = count(options); + const words = generate({ seed: "seed1", min, max }); + const words2 = generate({ seed: "seed2", min, max }); - assert.ok(totalWords === 0 , "total number of words should be 0 when no words satisfy the length criteria"); - }); - it("should return 0 when minLength is greater than maxLength", function () { - const options = { minLength: 10, maxLength: 5 }; - const totalWords = count(options); + // with these seeds, the number of words should 5 and 3 + assert.ok(words.length !== words2.length, "number of words is different"); + }); + }); + describe("with a custom word list", () => { + it("should return one word when called with wordList: customWordList", () => { + const word = generate({ wordList: customWordList }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }); + it("should return between 5 and 10 words when called with min: 5, max: 10, and wordList: customWordList", () => { + const words = generate({ min: 5, max: 10, wordList: customWordList }); + assert.ok(words.length >= 5 && words.length <= 10); + }); + it("should return one word with a minimum of 8 letters from a custom word list", () => { + const minWordSize = 8; + const word = generate({ minLength: minWordSize, wordList: customWordList }); - assert.ok(totalWords === 0 , "total number of words should be 0 when minLength is greater than maxLength"); - }); - it("should return the default count when incorrect arguments are provided", function () { - const options = "Illegal arguments"; - const totalWords = count(options); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length >= minWordSize, "result is less than 8 letters"); + }); + describe("must return one word even when called with a malformed (not string[]) custom word list", () => { + it("when the custom word list is empty", () => { + const word = generate({ wordList: [] }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + assert.ok(word.indexOf(" ") === -1, "word does not contain spaces"); + }); + it("when the custom word list is an `undefined`", () => { + const word = generate({ wordList: undefined }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is a `null`", () => { + const word = generate({ wordList: null }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is a `number`", () => { + const word = generate({ wordList: 123456 }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is a `string`", () => { + const word = generate({ wordList: "adasdsdas" }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is an `object`", () => { + const word = generate({ wordList: {} }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is a mixed array with no `string`", () => { + const word = generate({ wordList: [[], undefined, null, 123456] }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length, "word is not empty"); + }) + it("when the custom word list is a mixed array with one `string`", () => { + const word = generate({ wordList: [[], undefined, null, 123456, "test"] }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word === "test", "word is \"test\""); + assert.ok(word.length, "word is not empty"); + }) + }); + it("must return a word even with passing a number larger than the longest word in a custom word list to minLength", () => { + const minWordSize = getLongestWordLengthFromList(customWordList) + 1; + const word = generate({ minLength: minWordSize, wordList: customWordList }); - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); - }); - it("should treat non-number minLength as default and return the correct count", function () { - const options = { minLength: "5" }; - const totalWords = count(options); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length >= (minWordSize - 1), "result is less than 8 letters"); + }); + it("should return one word with a maximum of 5 letters from a custom word list", () => { + const maxWordSize = 5; + const word = generate({ maxLength: maxWordSize, wordList: customWordList }); - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); - }); - it("should ignore other options and return the count based on minLength and maxLength only", function () { - const options = { minLength: 4, maxLength: 7, separator: "-", formatter: (word) => word.toUpperCase(), seed: "random" }; - const totalWords = count(options); - - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); + assert.ok(typeof word === "string", "word is a string"); + assert.ok(word.length <= maxWordSize, "result exceeded 5 letters"); + }); + it("should return one word with the length between 3 and 5 from a custom word list", () => { + const minLengthSize = 3; + const maxLengthSize = 5; + const word = generate({ + minLength: minLengthSize, + maxLength: maxLengthSize, + wordList: customWordList + }); + assert.ok(typeof word === "string", "word is a string"); + assert.ok( + word.length >= minLengthSize && word.length <= maxLengthSize, + "result is not between the limit of 3 and 5" + ); + }); + }); }); - it("should return 0 when negative minLength and maxLength are passed", function () { - const options = { minLength: -20, maxLength: -10 }; - const totalWords = count(options); - assert.ok(totalWords === 0 , "total number of words should be 0 when no words satisfy the length criteria"); - }); - it("should return the correct count when minLength is -1 and maxLength is 10", function () { - const options = { minLength: -1, maxLength: 10 }; - const totalWords = count(options); + describe("count()", () => { + describe("with the default word list", () => { + it("should return the correct count when no options are provided", () => { + const totalWords = count(); + assert.ok(typeof totalWords === "number" && totalWords === wordList.length , "total number of words is a number and is not 0"); + }); + it("should return the correct count when minLength and maxLength options are provided", () => { + const totalWords = count({ minLength: 5, maxLength: 8 }); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + it("should return the correct count when only minLength option is provided", () => { + const totalWords = count({ minLength: 8 }); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + it("should return 0 when no word satisfy the length criteria", () => { + const totalWords = count({ minLength: 30, maxLength: 35 }); + assert.ok(totalWords === 0 , "total number of words should be 0 when no words satisfy the length criteria"); + }); + it("should return 0 when minLength is greater than maxLength", () => { + const totalWords = count({ minLength: 10, maxLength: 5 }); + assert.ok(totalWords === 0 , "total number of words should be 0 when minLength is greater than maxLength"); + }); + it("should return the default count when incorrect arguments are provided", () => { + const totalWords = count("Illegal arguments"); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + it("should ignore a non-number minLength and return the correct count", () => { + const totalWords = count({ minLength: "5" }); + assert.ok(typeof totalWords === "number" && totalWords === wordList.length , "total number of words is a number and is not 0"); + }); + it("should ignore other options and return the count based on minLength and maxLength only", () => { + const options = { minLength: 4, maxLength: 7, separator: "-", formatter: (word) => word.toUpperCase(), seed: "random" }; + const totalWords = count(options); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + it("should return 0 when negative minLength and maxLength are passed", () => { + const totalWords = count({ minLength: -20, maxLength: -10 }); + assert.ok(totalWords === 0 , "total number of words should be 0 when no words satisfy the length criteria"); + }); + it("should return the correct count when minLength is -1 and maxLength is 10", () => { + const totalWords = count({ minLength: -1, maxLength: 10 }); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + }); - assert.ok(typeof totalWords === "number" && totalWords != 0 , "total number of words is a number and is not 0"); + describe("with a custom word list", () => { + it("should return the correct count when a custom word list is provided", () => { + const totalWords = count({ wordList: customWordList }); + assert.ok(typeof totalWords === "number" && totalWords === customWordList.length, "total number of words is a number and is not 0"); + }); + it("should return the correct count when minLength, maxLength, and wordList options are provided", () => { + const totalWords = count({ minLength: 5, maxLength: 8, wordList: customWordList }); + assert.ok(typeof totalWords === "number" && totalWords !== 0 , "total number of words is a number and is not 0"); + }); + it("should return 0 when no word in the custom list satisfy the length criteria", () => { + const totalWords = count({ minLength: 30, maxLength: 35, wordList: customWordList }); + assert.ok(totalWords === 0 , "total number of words should be 0 when no words satisfy the length criteria"); + }); + it("should return 0 when minLength is greater than maxLength", () => { + const totalWords = count({ minLength: 10, maxLength: 5, wordList: customWordList }); + assert.ok(totalWords === 0 , "total number of words should be 0 when minLength is greater than maxLength"); + }); + }); }); -}) \ No newline at end of file +})