All tests pass on Windows

parent 49fb0e0f
...@@ -63,11 +63,23 @@ class Site { ...@@ -63,11 +63,23 @@ class Site {
static RELEASE_CHANNEL = { static RELEASE_CHANNEL = {
alpha : 'alpha', alpha : 'alpha',
beta : 'beta', beta : 'beta',
release: 'release' release: 'release',
npm: 'npm'
} }
static readAndCacheManifest () { static readAndCacheManifest () {
this.#manifest = JSON.parse(fs.readFileSync(path.join(__dirname, './manifest.json'), 'utf-8')) try {
this.#manifest = JSON.parse(fs.readFileSync(path.join(__dirname, 'manifest.json'), 'utf-8'))
} catch (error) {
// When running under Node (not wrapped as a binary), there will be no manifest file. So mock one.
this.#manifest = {
releaseChannel: 'npm',
binaryVersion: '20000101000000',
packageVersion: (require(path.join(__dirname, 'package.json'))).version,
sourceVersion: childProcess.execSync('git log -1 --oneline').toString().match(/^[0-9a-fA-F]{7}/)[0],
hugoVersion: (new Hugo()).version
}
}
} }
static getFromManifest (key) { static getFromManifest (key) {
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
"build-beta": "node bin/build.js --beta", "build-beta": "node bin/build.js --beta",
"deploy-beta": "bin/build.js --deploy --beta", "deploy-beta": "bin/build.js --deploy --beta",
"install-locally-beta": "node bin/build.js --install --beta", "install-locally-beta": "node bin/build.js --install --beta",
"test": "QUIET=true tape test/*.js | tap-spec", "test": "tape test/*.js | tap-spec",
"coverage": "QUIET=true nyc tape test/*.js | tap-nyc" "coverage": "nyc tape test/*.js | tap-nyc"
}, },
"funding": { "funding": {
"type": "foundation", "type": "foundation",
......
...@@ -4,6 +4,8 @@ const cli = require('../bin/lib/cli') ...@@ -4,6 +4,8 @@ const cli = require('../bin/lib/cli')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
process.env['QUIET'] = true
function loadPath(commandPath) { function loadPath(commandPath) {
fs.readFileSync(path.resolve(`${commandPath.replace('.', './bin')}.js`)) fs.readFileSync(path.resolve(`${commandPath.replace('.', './bin')}.js`))
} }
......
...@@ -13,9 +13,13 @@ ...@@ -13,9 +13,13 @@
const test = require('tape') const test = require('tape')
const childProcess = require('child_process') const childProcess = require('child_process')
const path = require('path')
const Site = require('../index.js') const Site = require('../index.js')
const Help = require('../bin/lib/Help') const Help = require('../bin/lib/Help')
const ensure = require('../bin/lib/ensure')
const Hugo = require('@small-tech/node-hugo')
process.env['QUIET'] = true
async function secureGet (url) { async function secureGet (url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -45,18 +49,47 @@ function options(timeout = 0) { ...@@ -45,18 +49,47 @@ function options(timeout = 0) {
return { env, timeout } return { env, timeout }
} }
function cliHeader() { function fundingMessage() {
const version = require('../package.json').version return dehydrate(`
return ` ╔═══════════════════════════════════════════╗
💕 Site.js v${version} (running on Node ${process.version}) ║ Like this? Fund us! ║
║ ║
╔═══════════════════════════════════════════╗ ║ We’re a tiny, independent not-for-profit. ║
║ Like this? Fund us! ║ ║ https://small-tech.org/fund-us ║
║ ║ ╚═══════════════════════════════════════════╝
║ We’re a tiny, independent not-for-profit. ║ `)
║ https://small-tech.org/fund-us ║ }
╚═══════════════════════════════════════════╝
` function siteJSHeader () {
return dehydrate('💕 Site.js')
}
function packageVersion () {
return require(path.join('..', 'package.json')).version
}
function binaryVersionLine () {
return dehydrate(`Version January 1st,2000 at 00:00:00 (${packageVersion()}-${gitHash()})`)
}
function nodeVersionLine () {
return dehydrate(`Node.js ${process.version.replace('v', '')}`)
}
function hugoVersionLine () {
return dehydrate(`Hugo ${(new Hugo()).version}`)
}
function gitHash () {
return childProcess.execSync('git log -1 --oneline').toString().match(/^[0-9a-fA-F]{7}/)[0]
}
function nexeBaseLink () {
return dehydrate(`https://sitejs.org/nexe/${process.platform}-${process.arch}-${process.version.replace('v', '')}`)
}
function sourceLink () {
return dehydrate(`https://source.small-tech.org/site.js/app/-/tree/${gitHash()}`)
} }
function dehydrate (str) { function dehydrate (str) {
...@@ -70,32 +103,62 @@ function outputForCommand(command) { ...@@ -70,32 +103,62 @@ function outputForCommand(command) {
return dehydrate(childProcess.execSync(command, options())) return dehydrate(childProcess.execSync(command, options()))
} }
function _(commandPartial) {
return `node ${path.join('bin', 'site.js')} ${commandPartial}`
}
test('[bin/commands] version', t => { test('[bin/commands] version', t => {
t.plan(1) t.plan(6)
const command = 'bin/site.js version'
const expectedOutput = dehydrate(cliHeader())
const command = _('version')
const actualOutput = outputForCommand(command) const actualOutput = outputForCommand(command)
t.strictEquals(actualOutput, expectedOutput, 'Actual output from command matches expected output') console.log(actualOutput)
console.log(binaryVersionLine())
console.log(nodeVersionLine())
console.log(sourceLink())
t.ok(actualOutput.includes(siteJSHeader()), 'version screen includes Site.js header')
t.ok(actualOutput.includes(binaryVersionLine()), 'version screen includes binary version line')
t.ok(actualOutput.includes(nodeVersionLine()), 'version screen includes Node.js version line')
t.ok(actualOutput.includes(hugoVersionLine()), 'version screen includes Hugo version line')
t.ok(actualOutput.includes(nexeBaseLink()), 'version screen includes nexe base link')
t.ok(actualOutput.includes(sourceLink()), 'version screen includes source link')
t.end() t.end()
}) })
test('[bin/commands] systemd startup daemon', t => { test('[bin/commands] systemd startup daemon', t => {
t.plan(19)
// //
// Commands used in the tests. // Commands used in the tests.
// //
const enableCommand = 'bin/site.js enable test/site' const enableCommand = _('enable test/site')
const disableCommand = 'bin/site.js disable' const disableCommand = _('disable')
const startCommand = 'bin/site.js start' const startCommand = _('start')
const stopCommand = 'bin/site.js stop' const stopCommand = _('stop')
const restartCommand = 'bin/site.js restart' const restartCommand = _('restart')
const statusCommand = 'bin/site.js status' const statusCommand = _('status')
// Startup daemons are only supported on platforms with systemd.
if (process.platform === 'win32' || process.platform === 'darwin' || !ensure.commandExists('systemctl')) {
const expectedErrorMessage = dehydrate('Sorry, daemons are only supported on Linux systems with systemd (systemctl required).')
const commandsToTest = ['enable', 'disable', 'start', 'stop', 'restart', 'status']
commandsToTest.forEach(commandName => {
try {
outputForCommand(eval(`${commandName}Command`))
} catch (error) {
t.ok(dehydrate(error.output[1].toString()).includes(expectedErrorMessage), 'On non-supported systems, daemon command ${commandName} fails gracefully as expected')
}
})
t.end()
return
}
t.plan(19)
// //
// Setup. // Setup.
...@@ -613,29 +676,38 @@ test('[commands] help', t => { ...@@ -613,29 +676,38 @@ test('[commands] help', t => {
}) })
test('[commands] hugo', t => { test('[commands] hugo', t => {
t.plan(1) t.plan(3)
const expectedOutput = dehydrate(`
${cliHeader()}
🎠 ❨Site.js❩ Running Hugo with command version
🅷 🆄 🅶 🅾 Hugo Static Site Generator v0.64.1-C327E75D linux/amd64 BuildDate: 2020-02-09T20:47:32Z let platform = process.platform
🅷 🆄 🅶 🅾 if (platform === 'win32') platform = 'windows'
💕 ❨Site.js❩ Goodbye! const actualOutput = outputForCommand(_('hugo version'))
`)
const actualOutput = outputForCommand('bin/site.js hugo version') t.ok(actualOutput.includes(dehydrate('🎠 ❨Site.js❩ Running Hugo with command version')), 'hugo command output includes Site.js information line')
t.ok(actualOutput.includes(dehydrate('🅷 🆄 🅶 🅾 Hugo Static Site Generator v0.64.1-C327E75D')), 'hugo command output includes correct hugo version')
t.strictEquals(actualOutput, expectedOutput, 'Actual output matches expected output') t.ok(actualOutput.includes(dehydrate('💕 ❨Site.js❩ Goodbye!')), 'hugo commands exits as expected')
t.end() t.end()
}) })
test('[commands] logs', async t => { test('[commands] logs', async t => {
// Startup daemons are only supported on platforms with systemd.
if (process.platform === 'win32' || process.platform === 'darwin' || !ensure.commandExists('systemctl')) {
const errorMessage = 'Sorry, daemons are only supported on Linux systems with systemd (journalctl required).'
try {
childProcess.exec(_('logs'))
} catch (error) {
t.ok(dehydrate(error.output[1].toString()).includes(errorMessage), 'On non-supported systems, daemon command logs fails gracefully as expected')
}
t.end()
return
}
t.plan(5) t.plan(5)
const optionsWithOneSecondTimeout = options(1000) const optionsWithOneSecondTimeout = options(1000)
childProcess.exec('bin/site.js logs', optionsWithOneSecondTimeout, (error, stdout, stderr) => { childProcess.exec(_('logs'), optionsWithOneSecondTimeout, (error, stdout, stderr) => {
// This will end with an error due to the timeout. Ensure that the error is the one we expect. // This will end with an error due to the timeout. Ensure that the error is the one we expect.
t.true(error, 'process termination is as expected') t.true(error, 'process termination is as expected')
......
...@@ -21,6 +21,8 @@ const queryString = require('querystring') ...@@ -21,6 +21,8 @@ const queryString = require('querystring')
const WebSocket = require('ws') const WebSocket = require('ws')
process.env['QUIET'] = true
function localhost(path) { function localhost(path) {
return `https://localhost${path}` return `https://localhost${path}`
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment