Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - oclif/fancy-test: extends mocha with helpful, chainable extensions · GitHub
Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Repository files navigation

fancy-test

⚠️This library has been deprecated.⚠️

As of v4@oclif/test no longer depends on fancy-test so we've deprecated the npm package.

extendable utilities for testing

VersionKnown VulnerabilitiesDownloads/weekLicense

Why

Mocha out of the box often requires a lot of setup and teardown code in beforeEach/afterEach filters. Using this library, you can get rid of those entirely and build your tests declaratively by chaining functionality together. Using the builtin plugins and your own, you create bits of functionality and chain them together with a concise syntax. It will greatly reduce the amount of repetition in your codebase.

It should be compatible with other testing libraries as well (e.g. jest), but may require a couple small changes. If you're interested, try it out and let me know if it works.

As an example, here is what a test file might look like for an application setup with fancy-test. This chain could partially be stored to a variable for reuse.

describe('api',()=>{fancy// [custom plugin] initializes the db.initDB({withUser: mockDBUser})// [custom plugin] uses nock to mock out github API.mockGithubAPI({user: mockGithubUser})// [custom plugin] that calls the API of the app.call('POST','/api/user/foo',{id: mockDBUser.id})// add adds to the context object// fetch the newly created data from the API (can return a promise).add('user',ctx=>ctx.db.fetchUserAsync(mockDBUser.id))// do just runs arbitary code// check to ensure the operation was successful.do(ctx=>expect(ctx.user.foo).to.equal('bar'))// it is essentially mocha's it(expectation, callback)// start the test and provide a description.it('POST /api/user/foo updates the user')})

V3 Breaking Changes

Version 3 now uses sinon under the hood to manage stubs. Because of this stubs are now set like this:

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Usage

Setup is pretty easy, just install mocha and fancy-test, then you can use any of the examples below.

Assume the following is before all the examples:

import{fancy}from'fancy-test'import{expect}from'chai'

Stub

Stub any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs.

import*asosfrom'os'describe('stub tests',()=>{fancy.stub(os,'platform',stub=>stub.returns('foobar')).it('sets os',()=>{expect(os.platform()).to.equal('foobar')})})

Catch

catch errors in a declarative way. By default, ensures they are actually thrown as well.

describe('catch tests',()=>{fancy.do(()=>{thrownewError('foobar')}).catch(/foo/).it('uses regex')fancy.do(()=>{thrownewError('foobar')}).catch('foobar').it('uses string')fancy.do(()=>{thrownewError('foobar')}).catch(err=>expect(err.message).to.match(/foo/)).it('uses function')fancy// this would normally raise because there is no error being thrown.catch('foobar',{raiseIfNotThrown: false}).it('do not error if not thrown')})

Without fancy, you could check an error like this:

it('dont do this',()=>{try{myfunc()}catch(err){expect(err.message).to.match(/mycustomerrorr/)}})

But this has a common flaw, if the test does not error, the test will still pass. Chai and other assertion libraries have helpers for this, but they still end up with somewhat messy code.

Finally

Run a task even if the test errors out.

describe('finally tests',()=>{fancy.do(()=>{thrownewError('x')}).finally(()=>{/* always called */}).end('always calls finally')})

Nock

Uses nock to mock out HTTP calls to external APIs. You'll need to also install nock in your devDependencies. Automatically calls done() to ensure the calls were made and cleanAll() to remove any pending requests.

constfancy=require('fancy-test')describe('nock tests',()=>{fancy.nock('https://api.github.com',api=>api.get('/me').reply(200,{name: 'jdxcode'})).it('mocks http call to github',async()=>{const{body: user}=awaitHTTP.get('https://api.github.com/me')expect(user).to.have.property('name','jdxcode')})})

Environment Variables

Sometimes it's helpful to clear out environment variables before running tests or override them to something common.

describe('env tests',()=>{fancy.env({FOO: 'BAR'}).it('mocks FOO',()=>{expect(process.env.FOO).to.equal('BAR')expect(process.env).to.not.deep.equal({FOO: 'BAR'})})fancy.env({FOO: 'BAR'},{clear: true}).it('clears all env vars',()=>{expect(process.env).to.deep.equal({FOO: 'BAR'})})})

Do

Run some arbitrary code within the pipeline. Useful to create custom logic and debugging.

describe('run',()=>{fancy.stdout().do(()=>console.log('foo')).do(({stdout})=>expect(stdout).to.equal('foo\n')).it('runs this callback last',()=>{// test code})// add to context objectfancy.add('a',()=>1).add('b',()=>2)// context will be {a: 1, b: 2}.it('does something with context',context=>{// test code})})

Add

Similar to run, but extends the context object with a new property. Can return a promise or not.

describe('add',()=>{fancy.add('foo',()=>'foo').add('bar',()=>Promise.resolve('bar')).do(ctx=>expect(ctx).to.include({foo: 'foo',bar: 'bar'})).it('adds the properties')})

Stdin Mocking

Mocks stdin. You may have to pass a delay to have it wait a bit until it sends the event.

describe('stdin test',()=>{fancy.stdin('whoa there!\n').stdout().it('mocks',()=>{process.stdin.setEncoding('utf8')process.stdin.once('data',data=>{// data === 'whoa there!\n'})})})

Stdout/Stderr Mocking

This is used for tests that ensure that certain stdout/stderr messages are made. By default this also trims the output from the screen. See the output by setting TEST_OUTPUT=1, or by setting {print: true} in the options passed.

You can use the library stdout-stderr directly for doing this, but you have to be careful to always reset it after the tests run. We do that work for you so you don't have to worry about mocha's output being hidden.

describe('stdmock tests',()=>{fancy.stdout().it('mocks stdout',output=>{console.log('foobar')expect(output.stdout).to.equal('foobar\n')})fancy.stderr().it('mocks stderr',output=>{console.error('foobar')expect(output.stderr).to.equal('foobar\n')})fancy.stdout().stderr().it('mocks stdout and stderr',output=>{console.log('foo')console.error('bar')expect(output.stdout).to.equal('foo\n')expect(output.stderr).to.equal('bar\n')})})

Done

You can get the mocha done() callback by passing in a second argument.

describe('calls done',()=>{fancy.it('expects FOO=bar',(_,done)=>{done()})})

Retries

Retry the test n times.

letcount=3describe('test retries',()=>{fancy.retries(2).do(()=>{count--if(count>0)thrownewError('x')}).it('retries 3 times')})

Timeout

Set mocha timeout duration.

constwait=(ms=10)=>newPromise(resolve=>setTimeout(resolve,ms))describe('timeout',()=>{fancy.timeout(50).it('times out after 50ms',async()=>{awaitwait(100)})})

Chai

This library includes chai for convenience:

import{expect,fancy}from'fancy-test'describe('has chai',()=>{fancy.env({FOO: 'BAR'}).it('expects FOO=bar',()=>{expect(process.env.FOO).to.equal('BAR')})})

Chaining

Everything here is chainable. You can also store parts of a chain to re-use later on.

For example:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).env({FOO: 'FOO'})setupDB.stdout().it('tests with stdout mocked',()=>{// test code})setupDB.env({BAR: 'BAR'}).it('also mocks the BAR environment variable',()=>{// test code})})

Using do you can really maximize this ability. In fact, you don't even need to pass a callback to it if you prefer this syntax:

describe('my suite',()=>{letsetupDB=fancy.do(()=>setupDB()).catch(/spuriousdberror/).do(()=>setupDeps())lettestMyApp=testInfo=>{returnsetupDB.run().do(context=>myApp(testInfo,context))}testMyApp({info: 'test run a'}).it('tests a')testMyApp({info: 'test run b'}).it('tests b')})

Custom Plugins

It's easy to create your own plugins to extend fancy. In oclif we use fancy to create custom command testers.

Here is an example that creates a counter that could be used to label each test run. See the actual test to see the TypeScript types needed.

letcount=0fancy=fancy.register('count',prefix=>{return{run(ctx){ctx.count=++countctx.testLabel=`${prefix}${count}`}}})describe('register',()=>{fancy.count('test-').it('is test #1',context=>{expect(context.count).to.equal(1)expect(context.testLabel).to.equal('test-1')})fancy.count('test-').it('is test #2',context=>{expect(context.count).to.equal(2)expect(context.testLabel).to.equal('test-2')})})

TypeScript

This module is built in typescript and exports the typings. Doing something with dynamic chaining like this was not easy, but it should be fully typed throughout. Look at the internal plugins to get an idea of how to keep typings for your custom plugins.

About

extends mocha with helpful, chainable extensions

Topics

Resources

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages