Comparing version 1.2.5 to 2.0.0
@@ -1,4 +0,8 @@ | ||
export declare function start(testFunction: Function): void | ||
export declare function stage(name: string): void | ||
export declare function test(condition: boolean, description?: string): void | ||
export declare function same(a: any, b: any, description?: string): void | ||
type TestFunctionArguments = { | ||
stage?: (name: string) => void, | ||
test?: (condition: boolean, description?: string) => void, | ||
same?: (a: any, b: any, description?: string) => void, | ||
different?: (a: any, b: any, description?: string) => void, | ||
} | ||
export default function start(testFunction: (arg: TestFunctionArguments) => void): void; | ||
export default function start(name: string, testFunction: (arg: TestFunctionArguments) => void): void; |
184
index.js
@@ -8,53 +8,88 @@ /*------------------------------------- | ||
const chalk = require('chalk') | ||
const deepEqual = require('./deepEqual.js') | ||
import chalk from 'chalk' | ||
import deepEqual from './deepEqual.js' | ||
if (!chalk) | ||
throw '`npm install` is necessary' | ||
class Stage { | ||
constructor(name) { | ||
this.name = name | ||
this.fails = [] | ||
} | ||
let | ||
currentStage = '', | ||
fails = [], | ||
totalErrors = 0, | ||
stageError = false, | ||
runningTest = false, | ||
testQueue = [] | ||
function happy() { | ||
const emojis = ['😏', '😄', '😃', '😉', '😊', '😋', '😌'] | ||
return emojis[Math.floor(Math.random() * emojis.length)] | ||
printResult() { | ||
if (this.fails.length) { | ||
console.log(chalk.bold.red("✗ "+this.name)) | ||
for (const fail of this.fails) | ||
console.log(chalk.gray(" Error at : " + chalk.reset.bold.red(fail))) | ||
} | ||
else | ||
console.log(chalk.green("✓ "+this.name)) | ||
} | ||
} | ||
function sad() { | ||
const emojis = ['😓', '😢', '😞', '😩', '😫'] | ||
return emojis[Math.floor(Math.random() * emojis.length)] | ||
} | ||
class Test { | ||
constructor(name, testFunction) { | ||
this.name = name && (' ' + chalk.underline(name)) | ||
this.testFunction = testFunction | ||
this.results = [] | ||
this.currentStage = null | ||
} | ||
async function start(testFunction) { | ||
if (runningTest) | ||
return testQueue.push(testFunction) | ||
runningTest = true | ||
fails() { | ||
let fails = 0 | ||
for (const result of this.results) { | ||
if (result instanceof Stage) | ||
fails += result.fails.length | ||
else fails++ | ||
} | ||
return fails | ||
} | ||
let {name} = testFunction | ||
if (name) { | ||
console.log(chalk.bold.blue("[ " + chalk.underline(name) + " ]")) | ||
async start() { | ||
try { | ||
await this.testFunction({ | ||
stage: this.stage.bind(this), | ||
test: this.test.bind(this), | ||
same: this.same.bind(this), | ||
different: this.different.bind(this), | ||
}) | ||
this.printResult() | ||
} | ||
catch (error) { | ||
this.printFatalError(error) | ||
} | ||
} | ||
try { | ||
await testFunction() | ||
endStage() | ||
printName() { | ||
if (this.name) | ||
console.log(chalk.bold.blue(`[${this.name} ]`)) | ||
} | ||
if (totalErrors == 1) | ||
console.log(chalk.bold.yellow(`One error occured during the test ${chalk.underline(name)} ${sad()}\n`)) | ||
else if (totalErrors > 1) | ||
console.log(chalk.bold.yellow(`${fails.length} errors occured during the test ${chalk.underline(name)} ${sad()}\n`)) | ||
printResult() { | ||
let fails = 0 | ||
this.printName() | ||
for (const result of this.results) { | ||
if (result instanceof Stage) { | ||
result.printResult() | ||
fails += result.fails.length | ||
} | ||
else { | ||
console.log(chalk.gray(" Error at : " + chalk.reset.bold.red(result))) | ||
fails++ | ||
} | ||
} | ||
if (fails == 1) | ||
console.log(chalk.bold.yellow(`One error occured during the test${this.name} ${sad()}\n`)) | ||
else if (fails > 1) | ||
console.log(chalk.bold.yellow(`${fails} errors occured during the test${this.name} ${sad()}\n`)) | ||
else | ||
console.log(chalk.bold.green(`The test ${name} has successfully passed ${happy()}\n`)) | ||
console.log(chalk.bold.green(`The test${this.name} has successfully passed ${happy()}\n`)) | ||
} | ||
catch (error) { | ||
console.log(chalk.bold.red(`✗ ${currentStage} : a critical error occured ${sad()} :`)) | ||
if (typeof error == 'object' && error instanceof Error) { | ||
console.log(error.message) | ||
printFatalError(error) { | ||
this.printName() | ||
console.log(chalk.bold.red(`✗ ${this.currentStage.name}`)) | ||
console.log(chalk.red(` A critical error occured ${sad()}`)) | ||
if (error instanceof Error) { | ||
console.log(chalk.bold.red(error.message)) | ||
if (error.code) | ||
@@ -67,51 +102,44 @@ console.log("Code error "+ error.code) | ||
} | ||
else | ||
console.log(error) | ||
console.log() | ||
else console.log(chalk.bold.red(error)) | ||
console.log() // newline | ||
} | ||
// next test function | ||
const next = testQueue.shift() | ||
currentStage = '' | ||
totalErrors = 0 | ||
stageError = false | ||
runningTest = false | ||
stage(name='') { | ||
this.currentStage = new Stage(name) | ||
this.results.push(this.currentStage) | ||
} | ||
if (next) | ||
start(next) | ||
} | ||
test(condition, description = '') { | ||
if (condition) return | ||
if (this.currentStage) this.currentStage.fails.push(description) | ||
else this.results.push(description) | ||
} | ||
function stage(newStage) { | ||
endStage() | ||
currentStage = newStage | ||
} | ||
same(a, b, description) { | ||
this.test(deepEqual(a, b), description) | ||
} | ||
function endStage() { | ||
if (!currentStage) | ||
return | ||
if (fails.length) { | ||
console.log(chalk.bold.red("✗ "+currentStage)) | ||
for (const fail of fails) | ||
console.log(chalk.gray(" Error at : " + chalk.reset.bold.red(fail))) | ||
fails.length = 0 | ||
totalErrors++ | ||
different(a, b, description) { | ||
this.test(!deepEqual(a, b), description) | ||
} | ||
else | ||
console.log(chalk.green("✓ "+currentStage)) | ||
} | ||
function test(conditionA, description='') { | ||
if (!conditionA) fails.push(testDescription) | ||
function happy() { | ||
const emojis = ['😏', '😄', '😃', '😉', '😊', '😋', '😌'] | ||
return emojis[Math.floor(Math.random() * emojis.length)] | ||
} | ||
function same(valueA, valueB, description='') { | ||
test(deepEqual(valueA, valueB), description) | ||
function sad() { | ||
const emojis = ['😓', '😢', '😞', '😩', '😫'] | ||
return emojis[Math.floor(Math.random() * emojis.length)] | ||
} | ||
module.exports = { | ||
start, | ||
starTest: start, // for retro-compatibility | ||
stage, | ||
test, | ||
same, | ||
} | ||
function start(testName, testFunction) { | ||
if (typeof testName == 'function') { | ||
testFunction = testName | ||
testName = '' | ||
} | ||
new Test(testName, testFunction).start() | ||
} | ||
export default start |
{ | ||
"version": "1.2.5", | ||
"version": "2.0.0", | ||
"type": "module", | ||
"author": { | ||
@@ -4,0 +5,0 @@ "name": "Lepzulnag" |
115
README.md
*FAst and smaRT TESTing* | ||
.. for those who want to enjoy simple and colorful tests without having to learn the whole ecosystem of a rich test library. | ||
.. for those who want to enjoy simple and emoji-augmented tests without having to learn the whole ecosystem of a rich test library. | ||
 | ||
FarTest is an obvious, colorful and enjoyable test library for small applications. It does not do cool stuff like code coverage, but you'll learn to use in no time. | ||
 | ||
## Installation | ||
@@ -16,45 +16,65 @@ | ||
## Usage | ||
FarTest simplest API export four functions : | ||
FarTest simplest API export one main function : | ||
- `start(test: Function)` - start a new test, | ||
- `stage(name: string)` - define the current stage inside a test, | ||
- `test(condition: boolean, name?: string)` - check an assertion inside a test. If `condition` is `true` then the assertion has succeeded, otherwise it failed. | ||
```ts | ||
function start(testName?: string, testFunction: ({ | ||
test?: (condition: boolean, description?: string) => void, | ||
same?: (a: any, b: any, description?: string) => void, | ||
stage?: (name: string) => void, | ||
}) => void): void | ||
``` | ||
The `testName` parameter is optional but strongly recommanded if you run multiple tests. | ||
The `testFunction` parameter is a function that can take four function arguments : | ||
- `test(condition: boolean, description?: string)` - a general assertion checking. If `condition` is `true` then the assertion has succeeded, otherwise it failed. | ||
- `same(a: any, b: any, name?: string)` - check if two values are the same. When `a` and `b`are objects, execute a deep comparison. Values can be of any type : numbers, strings, arrays, maps, sets, ... | ||
- `different(a: any, b: any, name?: string)` - opposite of `same` ; check if two values are strictly unequal. | ||
- `stage(name: string)` - use it to group unit tests together. | ||
And that's all. | ||
And that's the whole API. | ||
First, let's import the functions we need : | ||
```javascript | ||
import { start, stage, test } from 'fartest' | ||
``` | ||
Then, we start the test : | ||
```javascript | ||
### Basic example | ||
Let's create a new test file (can be in Typescript or in pure JS) : | ||
```ts | ||
import start from 'fartest' | ||
// the name of the function (MyAwesomeTest) is the name of the test | ||
// and is optional | ||
start(async function MyAwesomeTest() { | ||
start('My test', async function({stage, test, same, different}) { | ||
stage('Basic tests') | ||
test(1 == "1", "String and integer loose comparison") | ||
// we define the current stage of our test | ||
stage('Some succesful tests') | ||
// simple assertion | ||
test(1 == "1") | ||
// will fail | ||
test(1 === "1", "String and integer strict comparison") | ||
// will fail as well | ||
same(1, "1", "String and integer strict comparison (using same)") | ||
// the test description will be displayed in case of error | ||
test(21 == "21", "Test description") | ||
stage('Comparing objects') | ||
// deep comparison is done | ||
same({x: 1, y: 2}, {x: 1, y: 2}, "Deep object comparison") | ||
// the object type is also checked | ||
different(['foo'], {0: 'foo'}, "Array is not an object") | ||
}) | ||
``` | ||
stage('A simple test which will not succeed') | ||
test(21 === "21", "Test description") // will fail because types don't match | ||
same(21, "21", "Test description") // will fail as well | ||
Then run it using `node` or a tool like [esrun](https://www.npmjs.com/package/@digitak/esrun) if your file is written in Typescript or in modern JS. | ||
stage('Comparing objects') | ||
same({x: 1, y: 2}, {x: 1, y: 2}) // will pass | ||
 | ||
stage('Comparing object and array') | ||
same(['foo'], {0: 'foo'}) // will not pass | ||
### Critical errors | ||
stage('Crash test') | ||
undefined.coco = 321321 // any invalid code will be caught | ||
Any invalid code will be caught and printed as a critical error. | ||
```ts | ||
start('Bold test', async function({stage, test}) { | ||
stage('It gotta works!!') | ||
undefined.x == 12 | ||
}) | ||
``` | ||
 | ||
### Test asynchronous functions | ||
@@ -64,30 +84,27 @@ | ||
## Running multiple tests | ||
You can run multiple tests at once, in which case they will be executed one after another : | ||
```javascript | ||
You can run multiple tests at once, in which case they all will be executed simultaneously - the fastest tests will display their results first. | ||
```ts | ||
// test 1 | ||
start(async function CoolTest() { | ||
stage('1 == 1') | ||
test(1 == 1) // ok, pass | ||
stage('2 == "2"') | ||
test(2 == "2") // also pass because non-strict equality | ||
stage('same(2, "2")') | ||
same(2, "2") // does not pass | ||
start('Slow test', async function({stage, test}) { | ||
stage('Basic tests') | ||
// let's wait 1 second | ||
await new Promise(resolve => setTimeout(resolve, 1000)) | ||
test(1 == "1", "String and integer loose comparison") | ||
}) | ||
// test 2 | ||
start(async function SuperCoolTest() { | ||
stage('3 == 3') | ||
test(3 == 3) | ||
start('Instant test', async function({stage, test}) { | ||
stage('Basic tests') | ||
test(1 == "1", "String and integer loose comparison") | ||
}) | ||
``` | ||
 | ||
### Conclusion | ||
Congratulations! You've learned a new test library in less that 5 minutes! | ||
Congratulations, you've learned a new test library in less that 5 minutes! | ||
What you waiting for? | ||
What are you waiting for? | ||
*Let's FarT!* | ||
Enjoy testing 😌 |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
280255
178
109
Yes
1