Compare commits

..

No commits in common. "master" and "v1.0.4" have entirely different histories.

14 changed files with 176 additions and 302 deletions

View File

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

View File

@ -11,8 +11,6 @@
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg [standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[standard-url]: https://standardjs.com [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. 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.
## Install ## Install
@ -21,13 +19,20 @@ This is an open source funding experiment! The current model of sustaining open
npm install funding npm install funding
``` ```
### UPDATE: The experiment is over Feross posted [a recap](https://feross.org/funding-experiment-recap/) on his blog - Maintainers: [Apply to join the experiment](https://forms.gle/4agtnXJLS9E6qLaX9)
- Companies: [Post a terminal ad](https://forms.gle/hoJ9fJhP86kSGcvT6)
## See it action
```bash
npx funding
```
## What is this? ## What is this?
This is an open source funding experiment! ✨ 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. 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. 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.
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. 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.
@ -41,8 +46,6 @@ This experiment is currently running on a few open source projects that [Feross]
- [`standard`](https://standardjs.com) - [`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? ## Who is Feross?
Hey there, I'm Feross! Hey there, I'm Feross!
@ -68,11 +71,3 @@ The funds raised so far ($2,000) have paid for Feross's time to [release Standar
## Where can I provide feedback about this experiment? ## Where can I provide feedback about this experiment?
You can open an issue. But please be kind. I'm a human with feelings. ❤️ 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 { try {
if (funding) { if (funding) {
funding.printMessage() funding.printRandomMessage()
} }
} catch (err) { } catch (err) {
console.error(err.stack || err.message || err) console.error(err.stack || err.message || err)

View File

@ -8,20 +8,18 @@ const {
isSilentMode isSilentMode
} = require('./lib/detect') } = require('./lib/detect')
const { isShownRecently, markShown } = require('./lib/limit') const check = require('./lib/check')
const { checkMessage } = require('./lib/check')
const messages = require('./messages.json') const messages = require('./messages.json')
const wrap = require('./lib/wrap') const wrap = require('./lib/wrap')
function formatTitle (title) { function formatTitle (title) {
title = wrap(title) title = wrap(title)
if (!isCI()) { if (!isCI) {
title = chalk.black(title) title = chalk.black(title)
} }
if (!isHyper() && !isITerm()) { if (!isITerm && !isHyper) {
title = chalk.bold(title) title = chalk.bold(title)
} }
@ -36,7 +34,7 @@ function formatText (text) {
(match, url) => chalk.blue.underline(url) (match, url) => chalk.blue.underline(url)
) )
if (!isCI()) { if (!isCI) {
text = chalk.black(text) text = chalk.black(text)
} }
@ -48,6 +46,21 @@ function formatUrl (url) {
return chalk.blue.underline(url) return chalk.blue.underline(url)
} }
function checkMessage (message) {
const { title, text, url } = message
// Check if the strings are safe to print to the terminal. Specifically, the
// string should be plain ASCII, excluding control characters. This is
// paranoid and not strictly necessary since (1) we curate the messages.json
// file by hand and will never include non-ASCII text, and (2) we check the
// 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 formatMessage (message) { function formatMessage (message) {
const { title, text, url } = message const { title, text, url } = message
@ -74,7 +87,7 @@ function formatMessage (message) {
} }
} }
if (!isCI()) { if (!isCI) {
Object.assign(opts, { Object.assign(opts, {
backgroundColor: 'white' backgroundColor: 'white'
}) })
@ -83,37 +96,19 @@ function formatMessage (message) {
return boxen(coloredMessage, opts) return boxen(coloredMessage, opts)
} }
function printMessage () { function printRandomMessage () {
// Do not print message when npm is run in silent mode if (isSilentMode) return
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 i = Math.floor(Math.random() * messages.length)
const message = messages[i] 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
// paranoid and not strictly necessary since (1) we curate the messages.json
// file by hand and will never include non-ASCII text, and (2) we check the
// 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.
checkMessage(message) checkMessage(message)
// Format the message and print it
const formattedMessage = formatMessage(message) const formattedMessage = formatMessage(message)
console.log(formattedMessage + '\n') console.log(formattedMessage + '\n')
// Limit the frequency that messages are shown
markShown()
} }
module.exports = { module.exports = {
printMessage checkMessage,
formatMessage,
printRandomMessage
} }

View File

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

View File

@ -7,32 +7,23 @@ const ciInfo = require('ci-info')
const { const {
TERM_PROGRAM, TERM_PROGRAM,
npm_config_loglevel: NPM_CONFIG_LOGLEVEL, npm_config_loglevel: NPM_CONFIG_LOGLEVEL
OPEN_SOURCE_CONTRIBUTOR
} = process.env } = process.env
// Is Hyper (Mac)? // Is Hyper (Mac)?
const isHyper = () => TERM_PROGRAM === 'Hyper' const isHyper = TERM_PROGRAM === 'Hyper'
// Is iTerm.app (Mac)? // Is iTerm.app (Mac)?
const isITerm = () => TERM_PROGRAM === 'iTerm.app' const isITerm = TERM_PROGRAM === 'iTerm.app'
// Is Terminal.app (Mac)? // Is Terminal.app (Mac)?
const isTerminalApp = () => TERM_PROGRAM === 'Apple_Terminal' const isTerminalApp = TERM_PROGRAM === 'Apple_Terminal'
// Is CI? // Is CI?
const isCI = () => ciInfo.isCI const isCI = ciInfo.isCI
// Is silent mode enabled? // Is silent mode enabled?
const isSilentMode = () => ( const isSilentMode = ['silent', 'error', 'warn'].includes(NPM_CONFIG_LOGLEVEL)
['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 = { module.exports = {
isHyper, isHyper,

View File

@ -1,37 +0,0 @@
/**
* 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,7 +1,12 @@
[ [
{ {
"title": "npm install funding", "title": "Linode cloud computing",
"text": "I appreciate the thoughtful discussion and feedback from the community. I shared some thoughts about how this experiment went from my perspective.", "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://feross.org/funding-experiment-recap/" "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"
} }
] ]

View File

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

View File

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

View File

@ -2,109 +2,61 @@ const test = require('tape')
const cp = require('child_process') const cp = require('child_process')
const path = require('path') const path = require('path')
const { clearShown } = require('../lib/limit')
const FUNDING_BIN_PATH = path.join(__dirname, '..', 'bin', 'funding.js') const FUNDING_BIN_PATH = path.join(__dirname, '..', 'bin', 'funding.js')
test('Sanity check bin/funding.js output', t => { test('Sanity check bin/funding.js output', t => {
t.plan(4) t.plan(4)
clearShown()
cp.execFile(FUNDING_BIN_PATH, (err, stdout, stderr) => { cp.execFile(FUNDING_BIN_PATH, (err, stdout, stderr) => {
t.error(err) t.error(err)
t.ok(stdout.length > 0, 'there exists some stdout ouput') t.ok(stdout.length > 0, 'there exists some stdout ouput')
t.ok(!stdout.match(/error/gi), 'stdout output is not an error') t.ok(!stdout.match(/error/gi), 'stdout output is not an error')
t.equal(stderr, '', 'no stderr output') t.equal(stderr.length, 0, 'no stderr output')
}) })
}) })
test('`npm --silent` or `npm --loglevel silent` prevents output', t => { test('`npm --silent` or `npm --loglevel silent` prevents output', t => {
t.plan(3) t.plan(3)
clearShown()
const opts = { const opts = {
env: Object.assign({}, process.env, { env: {
...process.env,
npm_config_loglevel: 'silent' npm_config_loglevel: 'silent'
}) }
} }
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => { cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err) t.error(err)
t.equal(stdout, '', 'no stdout ouput') t.equal(stdout.length, 0, 'no stdout ouput')
t.equal(stderr, '', 'no stderr output') t.equal(stderr.length, 0, 'no stderr output')
}) })
}) })
test('`npm --quiet` or `npm --loglevel warn` prevents output', t => { test('`npm --quiet` or `npm --loglevel warn` prevents output', t => {
if (process.version.startsWith('v6.')) { t.plan(3)
t.pass('Ignore `--loglevel warn` on Node 6 (it is the default)')
return t.end() const opts = {
} env: {
...process.env,
t.plan(3) npm_config_loglevel: 'warn'
}
clearShown() }
const opts = { cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
env: Object.assign({}, process.env, { t.error(err)
npm_config_loglevel: 'warn' t.equal(stdout.length, 0, 'no stdout ouput')
}) t.equal(stderr.length, 0, 'no stderr output')
} })
})
cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {
t.error(err) test('`npm --loglevel error` prevents output', t => {
t.equal(stdout, '', 'no stdout ouput') t.plan(3)
t.equal(stderr, '', 'no stderr output')
}) const opts = {
}) env: {
...process.env,
test('`npm --loglevel error` prevents output', t => { npm_config_loglevel: 'error'
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) => { cp.execFile(FUNDING_BIN_PATH, [], opts, (err, stdout, stderr) => {

View File

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

View File

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