Compare commits

..

31 Commits

Author SHA1 Message Date
Feross Aboukhadijeh 56f8887088
Merge pull request #31 from feross/greenkeeper/tape-5.0.0 2020-10-27 04:33:35 +01:00
greenkeeper[bot] 9460c64a43
chore(package): update tape to version 5.0.0 2020-04-24 22:08:04 +00:00
Feross Aboukhadijeh 58b090c51c 1.0.9 2019-08-28 21:00:05 -07:00
Feross Aboukhadijeh b12b311554 the funding experiment is over!
I appreciate the thoughtful discussion and feedback from the community. I shared some thoughts about how this experiment went from my perspective.

https://feross.org/funding-experiment-recap/
2019-08-28 20:59:40 -07:00
Feross Aboukhadijeh 4a04573bc8 Update README.md 2019-08-26 13:13:09 -07:00
Feross Aboukhadijeh 7238cd97d6 Update README.md 2019-08-24 22:39:29 -07:00
Feross Aboukhadijeh d07b9af505 1.0.8 2019-08-24 18:26:53 -07:00
Feross Aboukhadijeh 03937d3f11 Update messages.json 2019-08-24 18:26:31 -07:00
Feross Aboukhadijeh 2485ab553e npm start: clear shown first, for easier testing 2019-08-24 17:59:21 -07:00
Feross Aboukhadijeh 8115da8aa9 1.0.7 2019-08-24 16:46:27 -07:00
Feross Aboukhadijeh 427bb8ffb6 Update messages.json 2019-08-24 16:46:16 -07:00
Feross Aboukhadijeh 356655d985 OPEN_SOURCE_SUPPORTER -> OPEN_SOURCE_CONTRIBUTOR 2019-08-24 16:46:08 -07:00
Feross Aboukhadijeh 705e7c9c23
Merge pull request #9 from feross/silent
Silent when OPEN_SOURCE_SUPPORTER=true
2019-08-24 16:18:02 -07:00
Feross Aboukhadijeh 67746f78f2 syntax 2019-08-24 16:15:49 -07:00
Feross Aboukhadijeh ebce9a2a28 add disable instructions to readme 2019-08-24 16:12:09 -07:00
Feross Aboukhadijeh 4593e982d6 Silent when OPEN_SOURCE_SUPPORTER=true 2019-08-24 15:59:03 -07:00
Feross Aboukhadijeh 08601f8c52 1.0.6 2019-08-24 15:56:34 -07:00
Feross Aboukhadijeh b9a3b3802b Add deduplication
In case `funding` ends up in the dependency tree more than once, ensure that it only prints once.
2019-08-24 15:56:23 -07:00
Feross Aboukhadijeh 3134b34041 detect: export functions for consistency 2019-08-23 20:48:09 -07:00
Feross Aboukhadijeh 4399a0950a 1.0.5 2019-08-23 18:40:38 -07:00
Feross Aboukhadijeh 237caca5b8 Ignore `--loglevel warn` on Node 6 (it is the default) 2019-08-23 18:39:35 -07:00
Feross Aboukhadijeh b24ccba2bb test: Fix tests on Node 6 2019-08-23 18:17:57 -07:00
Feross Aboukhadijeh c5269f1fcb 1.0.4 2019-08-23 18:12:58 -07:00
Feross Aboukhadijeh 9627cd338b Respect npm loglevel settings
The following ways of reducing npm logging are now respected:

npm --silent
npm --loglevel silent
npm --quiet
npm --loglevel warn
npm --loglevel error
2019-08-23 17:57:53 -07:00
Feross Aboukhadijeh b2fbc11692 detect: Export booleans instead of functions 2019-08-23 17:56:55 -07:00
Feross Aboukhadijeh 2820d65c4a alphabetize exports 2019-08-23 17:31:38 -07:00
Feross Aboukhadijeh 7e286cbbed readme 2019-08-20 00:13:14 -07:00
Feross Aboukhadijeh 687ff1c077 readme 2019-08-20 00:11:15 -07:00
Feross Aboukhadijeh 748b703f62 1.0.3 2019-08-20 00:08:47 -07:00
Feross Aboukhadijeh 7eea1cb993 add links to forms 2019-08-20 00:08:28 -07:00
Feross Aboukhadijeh 86fbb555b2 fix formatting on travis and other CI services 2019-08-20 00:05:21 -07:00
14 changed files with 339 additions and 144 deletions

View File

@ -1,2 +1,3 @@
.travis.yml
test/
tools/

View File

@ -11,25 +11,29 @@
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[standard-url]: https://standardjs.com
### UPDATE: The experiment is over Feross posted [a recap](https://feross.org/funding-experiment-recap/) on his blog
This is an open source funding experiment! The current model of sustaining open source is not working. We desperately need more experimentation. This is one such experiment.
## Usage
## Install
```bash
npm install funding
```
### UPDATE: The experiment is over Feross posted [a recap](https://feross.org/funding-experiment-recap/) on his blog
## What is this?
This is an open source funding experiment! ✨
Whenever users install open source software, this package will display a message from a company that supports open source. Currently, these are [Linode](https://welcome.linode.com/standardjs) and [LogRocket](https://logrocket.com/term). The sponsorship pays directly for maintainer time. That is, writing new features, fixing bugs, answering user questions, and improving documentation.
Whenever users install open source software, this package will display a message from a company that supports open source. The sponsorship pays directly for maintainer time. That is, writing new features, fixing bugs, answering user questions, and improving documentation.
The goal is to make sure that packages are well-maintained now and for the foreseeable future, with regular releases, improved reliability, and timely security patches. Healthy open source packages benefit users and maintainers alike.
## What does this code do?
You can take a look! All the code is open source in this GitHub repository. Essentially, it calls `console.log()` on some text. There is no tracking, data collecting, or unexpected behavior. You can look at the code to verify indeed, this is the beauty of open source!
You can take a look! All the code is open source in this GitHub repository. Essentially, it calls `console.log()` on some text. **There is no tracking or data collecting — and it will always stay this way.** You can look at the code to verify indeed, this is the beauty of open source!
## Where is this experiment running?
@ -37,6 +41,8 @@ This experiment is currently running on a few open source projects that [Feross]
- [`standard`](https://standardjs.com)
### UPDATE: The experiment is over Feross posted [a recap](https://feross.org/funding-experiment-recap/) on his blog
## Who is Feross?
Hey there, I'm Feross!
@ -62,3 +68,11 @@ The funds raised so far ($2,000) have paid for Feross's time to [release Standar
## Where can I provide feedback about this experiment?
You can open an issue. But please be kind. I'm a human with feelings. ❤️
## How can I disable this?
Just to be super clear: **This package does no tracking or data collecting — and it will always stay this way.** It's just a fancy `console.log()`.
If you support open source through direct contributions, donations, or however else you see fit, you can permanently silence `funding` by adding an environment variable `OPEN_SOURCE_CONTRIBUTOR=true` to your terminal environment.
Note, `funding` also respects npm's `loglevel` setting, so e.g. `npm install --silent` and `npm install --quiet` will be respected.

View File

@ -15,7 +15,7 @@ try {
try {
if (funding) {
funding.printRandomMessage()
funding.printMessage()
}
} catch (err) {
console.error(err.stack || err.message || err)

View File

@ -1,20 +1,27 @@
const boxen = require('boxen')
const chalk = require('chalk')
const detect = require('./lib/detect')
const wrap = require('./lib/wrap')
const check = require('./lib/check')
const {
isHyper,
isITerm,
isCI,
isSilentMode
} = require('./lib/detect')
const { isShownRecently, markShown } = require('./lib/limit')
const { checkMessage } = require('./lib/check')
const messages = require('./messages.json')
const wrap = require('./lib/wrap')
function formatTitle (title) {
title = wrap(title)
if (!detect.isTravis()) {
if (!isCI()) {
title = chalk.black(title)
}
if (!detect.isITerm() && !detect.isHyper()) {
if (!isHyper() && !isITerm()) {
title = chalk.bold(title)
}
@ -29,7 +36,7 @@ function formatText (text) {
(match, url) => chalk.blue.underline(url)
)
if (!detect.isTravis()) {
if (!isCI()) {
text = chalk.black(text)
}
@ -49,7 +56,16 @@ function formatMessage (message) {
const opts = {
align: 'center',
borderStyle: {
topLeft: ' ',
topRight: ' ',
bottomLeft: ' ',
bottomRight: ' ',
horizontal: ' ',
vertical: ' '
},
float: 'center',
margin: 0,
padding: {
top: 1,
right: 4,
@ -58,32 +74,28 @@ function formatMessage (message) {
}
}
if (detect.isTravis()) {
if (!isCI()) {
Object.assign(opts, {
borderColor: 'green',
borderStyle: 'bold',
margin: 1
})
} else {
Object.assign(opts, {
borderStyle: {
topLeft: ' ',
topRight: ' ',
bottomLeft: ' ',
bottomRight: ' ',
horizontal: ' ',
vertical: ' '
},
backgroundColor: 'white',
margin: 0
backgroundColor: 'white'
})
}
return boxen(coloredMessage, opts)
}
function checkMessage (message) {
const { title, text, url } = message
function printMessage () {
// Do not print message when npm is run in silent mode
if (isSilentMode()) return
// Do not print message when one has been shown recently
if (isShownRecently()) return
// Skip running if no messages are available
if (messages.length === 0) return
// Select a random message
const i = Math.floor(Math.random() * messages.length)
const message = messages[i]
// Check if the strings are safe to print to the terminal. Specifically, the
// string should be plain ASCII, excluding control characters. This is
@ -92,22 +104,16 @@ function checkMessage (message) {
// strings at package publish time (see test/messages.js). But it doesn't hurt
// to check again in the client and assert that messages are plain ASCII. This
// is the security principle of defense-in-depth.
check(title)
check(text)
check(url)
}
function printRandomMessage () {
const i = Math.floor(Math.random() * messages.length)
const message = messages[i]
checkMessage(message)
// Format the message and print it
const formattedMessage = formatMessage(message)
console.log(formattedMessage + '\n')
// Limit the frequency that messages are shown
markShown()
}
module.exports = {
formatMessage,
checkMessage,
printRandomMessage
printMessage
}

View File

@ -18,4 +18,15 @@ function checkString (str) {
}
}
module.exports = checkString
function checkMessage (message) {
const { title, text, url } = message
checkString(title)
checkString(text)
checkString(url)
}
module.exports = {
checkString,
checkMessage
}

View File

@ -1,10 +1,15 @@
/**
* Functions to detect which Terminal emulator is in use.
* Functions to detect information about the environment, e.g. which Terminal
* emulator is in use, or whether silent mode is enabled.
*/
const ciInfo = require('ci-info')
const TERM_PROGRAM = process.env.TERM_PROGRAM
const {
TERM_PROGRAM,
npm_config_loglevel: NPM_CONFIG_LOGLEVEL,
OPEN_SOURCE_CONTRIBUTOR
} = process.env
// Is Hyper (Mac)?
const isHyper = () => TERM_PROGRAM === 'Hyper'
@ -15,12 +20,24 @@ const isITerm = () => TERM_PROGRAM === 'iTerm.app'
// Is Terminal.app (Mac)?
const isTerminalApp = () => TERM_PROGRAM === 'Apple_Terminal'
// Is Travis (CI)?
const isTravis = () => ciInfo.TRAVIS
// Is CI?
const isCI = () => ciInfo.isCI
// Is silent mode enabled?
const isSilentMode = () => (
['silent', 'error'].includes(NPM_CONFIG_LOGLEVEL) ||
(NPM_CONFIG_LOGLEVEL === 'warn' && !process.version.startsWith('v6.')) ||
isEnabled(OPEN_SOURCE_CONTRIBUTOR)
)
function isEnabled (value) {
return !!value && value !== '0' && value !== 'false'
}
module.exports = {
isHyper,
isITerm,
isTerminalApp,
isTravis
isCI,
isSilentMode
}

37
lib/limit.js Normal file
View File

@ -0,0 +1,37 @@
/**
* Functions to limit the frequency that messages are shown.
*/
const { tmpdir } = require('os')
const { statSync, unlinkSync, writeFileSync } = require('fs')
const { join } = require('path')
const LIMIT_FILE_PATH = join(tmpdir(), 'funding-message-shown')
const LIMIT_TIMEOUT = 60 * 1000 // 1 minute
function isShownRecently () {
try {
const { mtime: lastShown } = statSync(LIMIT_FILE_PATH)
return Date.now() - lastShown < LIMIT_TIMEOUT
} catch (e) {}
return false
}
function markShown () {
try {
writeFileSync(LIMIT_FILE_PATH, '')
} catch (err) {}
}
// Only used in tests
function clearShown () {
try {
unlinkSync(LIMIT_FILE_PATH)
} catch (err) {}
}
module.exports = {
isShownRecently,
markShown,
clearShown
}

View File

@ -1,12 +1,7 @@
[
{
"title": "Linode cloud computing",
"text": "Deploy a server in seconds with your choice of Linux distro, resources, and host location. For a $20 credit, enter promo code STANDARDJS19 at sign up.",
"url": "https://welcome.linode.com/standardjs"
},
{
"title": "LogRocket",
"text": "Stop guessing why bugs happen. LogRocket lets you replay what users do on your web app or website, helping you reproduce bugs and fix issues faster.",
"url": "https://logrocket.com/term"
"title": "npm install funding",
"text": "I appreciate the thoughtful discussion and feedback from the community. I shared some thoughts about how this experiment went from my perspective.",
"url": "https://feross.org/funding-experiment-recap/"
}
]

View File

@ -1,7 +1,7 @@
{
"name": "funding",
"description": "Get open source maintainers paid",
"version": "1.0.2",
"version": "1.0.9",
"author": {
"name": "Feross Aboukhadijeh",
"email": "feross@feross.org",
@ -22,7 +22,7 @@
},
"devDependencies": {
"standard": "*",
"tape": "^4.11.0"
"tape": "^5.0.0"
},
"homepage": "https://github.com/feross/funding",
"keywords": [
@ -39,7 +39,7 @@
},
"scripts": {
"postinstall": "node bin/funding.js",
"start": "node bin/funding.js",
"test": "standard && node bin/funding.js && tape test/*.js"
"start": "node tools/clear.js && node bin/funding.js",
"test": "standard && npm start && tape test/*.js"
}
}

View File

@ -1,146 +1,146 @@
const test = require('tape')
const check = require('../lib/check')
const { checkString } = require('../lib/check')
test('check() accepts valid strings', t => {
test('checkString() accepts valid strings', t => {
t.doesNotThrow(() => {
check('')
checkString('')
})
t.doesNotThrow(() => {
check('support open source')
checkString('support open source')
})
t.doesNotThrow(() => {
check('support open source\nbe a part of history')
checkString('support open source\nbe a part of history')
})
t.doesNotThrow(() => {
check('support open source\nbe a part of history\nmaintainers unite')
checkString('support open source\nbe a part of history\nmaintainers unite')
})
t.doesNotThrow(() => {
check('!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
checkString('!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
})
t.end()
})
test('check() behaves as expected on first 127 characters', t => {
test('checkString() behaves as expected on first 127 characters', t => {
// control characters not allowed
t.throws(() => { check('\u0000') })
t.throws(() => { check('\u0001') })
t.throws(() => { check('\u0002') })
t.throws(() => { check('\u0003') })
t.throws(() => { check('\u0004') })
t.throws(() => { check('\u0005') })
t.throws(() => { check('\u0006') })
t.throws(() => { check('\u0007') })
t.throws(() => { check('\u0008') })
t.throws(() => { check('\u0009') })
t.throws(() => { checkString('\u0000') })
t.throws(() => { checkString('\u0001') })
t.throws(() => { checkString('\u0002') })
t.throws(() => { checkString('\u0003') })
t.throws(() => { checkString('\u0004') })
t.throws(() => { checkString('\u0005') })
t.throws(() => { checkString('\u0006') })
t.throws(() => { checkString('\u0007') })
t.throws(() => { checkString('\u0008') })
t.throws(() => { checkString('\u0009') })
// newline is allowed
t.doesNotThrow(() => { check('\u000a') })
t.doesNotThrow(() => { checkString('\u000a') })
// control characters not allowed
t.throws(() => { check('\u000b') })
t.throws(() => { check('\u000c') })
t.throws(() => { check('\u000d') })
t.throws(() => { check('\u000e') })
t.throws(() => { check('\u000f') })
t.throws(() => { check('\u0010') })
t.throws(() => { check('\u0011') })
t.throws(() => { check('\u0012') })
t.throws(() => { check('\u0013') })
t.throws(() => { check('\u0014') })
t.throws(() => { check('\u0015') })
t.throws(() => { check('\u0016') })
t.throws(() => { check('\u0017') })
t.throws(() => { check('\u0018') })
t.throws(() => { check('\u0019') })
t.throws(() => { check('\u001a') })
t.throws(() => { check('\u001b') })
t.throws(() => { check('\u001c') })
t.throws(() => { check('\u001d') })
t.throws(() => { check('\u001e') })
t.throws(() => { check('\u001f') })
t.throws(() => { checkString('\u000b') })
t.throws(() => { checkString('\u000c') })
t.throws(() => { checkString('\u000d') })
t.throws(() => { checkString('\u000e') })
t.throws(() => { checkString('\u000f') })
t.throws(() => { checkString('\u0010') })
t.throws(() => { checkString('\u0011') })
t.throws(() => { checkString('\u0012') })
t.throws(() => { checkString('\u0013') })
t.throws(() => { checkString('\u0014') })
t.throws(() => { checkString('\u0015') })
t.throws(() => { checkString('\u0016') })
t.throws(() => { checkString('\u0017') })
t.throws(() => { checkString('\u0018') })
t.throws(() => { checkString('\u0019') })
t.throws(() => { checkString('\u001a') })
t.throws(() => { checkString('\u001b') })
t.throws(() => { checkString('\u001c') })
t.throws(() => { checkString('\u001d') })
t.throws(() => { checkString('\u001e') })
t.throws(() => { checkString('\u001f') })
// normal characters are allowed
for (let i = 0x20; i < 0x7f; i++) {
t.doesNotThrow(() => { check(Buffer.from([i]).toString()) })
t.doesNotThrow(() => { checkString(Buffer.from([i]).toString()) })
}
// del is not allowed
t.throws(() => { check('\u007f') })
t.throws(() => { checkString('\u007f') })
t.end()
})
test('check() rejects high code points', t => {
test('checkString() rejects high code points', t => {
// char codes 128-255 are not allowed
for (let i = 0x80; i <= 0xff; i++) {
t.throws(() => { check(Buffer.from([i]).toString()) })
t.throws(() => { checkString(Buffer.from([i]).toString()) })
}
// emojis are not allowed
t.throws(() => { check('💩') })
t.throws(() => { check('❤️') })
t.throws(() => { check('✨') })
t.throws(() => { checkString('💩') })
t.throws(() => { checkString('❤️') })
t.throws(() => { checkString('✨') })
// ansi escape sequences are not allowed
t.throws(() => { check('\u001B') })
t.throws(() => { check('\u001B[4mfoo\u001B[24m') })
t.throws(() => { check('\u001B[31mfoo\u001B[39m') })
t.throws(() => { check('\u001B[41mfoo\u001B[49m') })
t.throws(() => { check('\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39m') })
t.throws(() => { check('\u001B[31mfoo\u001B[4m\u001B[44mbar\u001B[49m\u001B[24m!\u001B[39m') })
t.throws(() => { check('\u001B[31ma\u001B[33mb\u001B[32mc\u001B[33mb\u001B[31mc\u001B[39m') })
t.throws(() => { check('\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m') })
t.throws(() => { checkString('\u001B') })
t.throws(() => { checkString('\u001B[4mfoo\u001B[24m') })
t.throws(() => { checkString('\u001B[31mfoo\u001B[39m') })
t.throws(() => { checkString('\u001B[41mfoo\u001B[49m') })
t.throws(() => { checkString('\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39m') })
t.throws(() => { checkString('\u001B[31mfoo\u001B[4m\u001B[44mbar\u001B[49m\u001B[24m!\u001B[39m') })
t.throws(() => { checkString('\u001B[31ma\u001B[33mb\u001B[32mc\u001B[33mb\u001B[31mc\u001B[39m') })
t.throws(() => { checkString('\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m') })
t.end()
})
test('check() accepts valid strings', t => {
test('checkString() accepts valid strings', t => {
// 20 lines, with 20 line max
t.doesNotThrow(() => {
check('a\nb\nc\nd\ne\nf\ng\nh\ni\nj\na\nb\nc\nd\ne\nf\ng\nh\ni\nj', 20)
checkString('a\nb\nc\nd\ne\nf\ng\nh\ni\nj\na\nb\nc\nd\ne\nf\ng\nh\ni\nj', 20)
})
t.end()
})
test('check() rejects invalid strings', t => {
test('checkString() rejects invalid strings', t => {
// 3 character line, followed by line with unsafe characters
t.throws(() => {
check('abc\ndef💩gih')
checkString('abc\ndef💩gih')
})
// two lines with invalid characters
t.throws(() => {
check('🌟\ndef💩gih')
checkString('🌟\ndef💩gih')
})
t.end()
})
test('check() rejects non-strings', t => {
test('checkString() rejects non-strings', t => {
// function argument
t.throws(() => {
check(() => {})
checkString(() => {})
})
// object argument
t.throws(() => {
check({})
checkString({})
})
// number argument
t.throws(() => {
check(42)
checkString(42)
})
// null argument
t.throws(() => {
check(null)
checkString(null)
})
// undefined argument
t.throws(() => {
check(undefined)
checkString(undefined)
})
t.end()

View File

@ -2,15 +2,114 @@ const test = require('tape')
const cp = require('child_process')
const path = require('path')
const { clearShown } = require('../lib/limit')
const FUNDING_BIN_PATH = path.join(__dirname, '..', 'bin', 'funding.js')
test('Santiy check bin/funding.js output', t => {
test('Sanity check bin/funding.js output', t => {
t.plan(4)
clearShown()
cp.execFile(FUNDING_BIN_PATH, (err, stdout, stderr) => {
t.error(err)
t.ok(stdout.length > 0, 'there exists some stdout ouput')
t.ok(!stdout.match(/error/gi), 'stdout output is not an error')
t.equal(stderr, '', 'no stderr output')
})
})
test('`npm --silent` or `npm --loglevel silent` prevents output', t => {
t.plan(3)
clearShown()
const opts = {
env: Object.assign({}, process.env, {
npm_config_loglevel: 'silent'
})
}
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err)
t.equal(stdout, '', 'no stdout ouput')
t.equal(stderr, '', 'no stderr output')
})
})
test('`npm --quiet` or `npm --loglevel warn` prevents output', t => {
if (process.version.startsWith('v6.')) {
t.pass('Ignore `--loglevel warn` on Node 6 (it is the default)')
return t.end()
}
t.plan(3)
clearShown()
const opts = {
env: Object.assign({}, process.env, {
npm_config_loglevel: 'warn'
})
}
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err)
t.equal(stdout, '', 'no stdout ouput')
t.equal(stderr, '', 'no stderr output')
})
})
test('`npm --loglevel error` prevents output', t => {
t.plan(3)
clearShown()
const opts = {
env: Object.assign({}, process.env, {
npm_config_loglevel: 'error'
})
}
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err)
t.equal(stdout, '', 'no stdout ouput')
t.equal(stderr, '', 'no stderr output')
})
})
test('deduplication / rate-limiting', t => {
t.plan(7)
clearShown()
cp.execFile(FUNDING_BIN_PATH, (err, stdout, stderr) => {
t.error(err)
t.ok(stdout.length > 0, 'there exists some stdout ouput')
t.ok(!stdout.match(/error/gi), 'stdout output is not an error')
t.equal(stderr, '', 'no stderr output')
// Second run should print nothing, since it was recently shown
cp.execFile(FUNDING_BIN_PATH, (err, stdout, stderr) => {
t.error(err)
t.equal(stdout, '', 'no stdout ouput')
t.equal(stderr, '', 'no stderr output')
})
})
})
test('OPEN_SOURCE_CONTRIBUTOR=true prevents output', t => {
t.plan(3)
const opts = {
env: Object.assign({}, process.env, {
OPEN_SOURCE_CONTRIBUTOR: 'true'
})
}
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err)
t.equal(stdout.length, 0, 'no stdout ouput')
t.equal(stderr.length, 0, 'no stderr output')
})
})

16
test/limit.js Normal file
View File

@ -0,0 +1,16 @@
const test = require('tape')
const { isShownRecently, markShown, clearShown } = require('../lib/limit')
test('shown file works', t => {
clearShown()
t.ok(!isShownRecently(), 'initially, not shown recently')
markShown()
t.ok(isShownRecently(), 'after markShown(), is shown recently')
clearShown()
t.ok(!isShownRecently(), 'after clearShown(), not shown recently')
t.end()
})

View File

@ -1,6 +1,6 @@
const test = require('tape')
const check = require('../lib/check')
const funding = require('../')
const { checkString, checkMessage } = require('../lib/check')
const messages = require('../messages.json')
test('Messages is in the expected shape', t => {
@ -17,26 +17,20 @@ test('Check all messages with check()', t => {
t.equal(typeof message.url, 'string')
t.doesNotThrow(() => {
check(message.title)
})
checkString(message.title)
}, 'checkString(message.title)')
t.doesNotThrow(() => {
check(message.text)
})
checkString(message.text)
}, 'checkString(message.text)')
t.doesNotThrow(() => {
check(message.url)
})
})
t.end()
})
test('Check all messages with checkMessage()', t => {
messages.forEach(message => {
t.doesNotThrow(() => {
funding.checkMessage(message)
})
checkString(message.url)
}, 'checkString(message.url)')
t.doesNotThrow(() => {
checkMessage(message)
}, 'checkMessage(message)')
})
t.end()

5
tools/clear.js Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env node
const { clearShown } = require('../lib/limit')
clearShown()