Skip to content

Commit 5df2b72

Browse files
Migration from tap to mocha (#2851)
* migrate from tap to mocha After make-fetch-happen update GitHub Actions started failing. Migrating from tap to mocha testing framework for GitHub Action stability. * write custom test reporter for more verbose output Implemented a simple custom mocha test reporter to replace the default one. Made test report more developer friendly.
1 parent aaa117c commit 5df2b72

13 files changed

+1773
-1835
lines changed

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@
3939
},
4040
"devDependencies": {
4141
"bindings": "^1.5.0",
42+
"mocha": "^10.2.0",
4243
"nan": "^2.14.2",
4344
"require-inject": "^1.4.4",
44-
"standard": "^14.3.4",
45-
"tap": "^12.7.0"
45+
"standard": "^14.3.4"
4646
},
4747
"scripts": {
4848
"lint": "standard */*.js test/**/*.js",
49-
"test": "npm run lint && tap --timeout=1200 test/test-*"
49+
"test": "npm run lint && mocha --reporter=test/reporter.js test/test-download.js test/test-*"
5050
}
5151
}

test/reporter.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const Mocha = require('mocha')
2+
3+
class Reporter {
4+
constructor (runner) {
5+
this.failedTests = []
6+
7+
runner.on(Mocha.Runner.constants.EVENT_RUN_BEGIN, () => {
8+
console.log('Starting tests')
9+
})
10+
11+
runner.on(Mocha.Runner.constants.EVENT_RUN_END, () => {
12+
console.log('Tests finished')
13+
console.log()
14+
console.log('****************')
15+
console.log('* TESTS REPORT *')
16+
console.log('****************')
17+
console.log()
18+
console.log(`Executed ${runner.stats.suites} suites with ${runner.stats.tests} tests in ${runner.stats.duration} ms`)
19+
console.log(` Passed: ${runner.stats.passes}`)
20+
console.log(` Skipped: ${runner.stats.pending}`)
21+
console.log(` Failed: ${runner.stats.failures}`)
22+
if (this.failedTests.length > 0) {
23+
console.log()
24+
console.log(' Failed test details')
25+
this.failedTests.forEach((failedTest, index) => {
26+
console.log()
27+
console.log(` ${index + 1}.'${failedTest.test.fullTitle()}'`)
28+
console.log(` Name: ${failedTest.error.name}`)
29+
console.log(` Message: ${failedTest.error.message}`)
30+
console.log(` Code: ${failedTest.error.code}`)
31+
console.log(` Stack: ${failedTest.error.stack}`)
32+
})
33+
}
34+
console.log()
35+
})
36+
37+
runner.on(Mocha.Runner.constants.EVENT_SUITE_BEGIN, (suite) => {
38+
if (suite.root) {
39+
return
40+
}
41+
console.log(`Starting suite '${suite.title}'`)
42+
})
43+
44+
runner.on(Mocha.Runner.constants.EVENT_SUITE_END, (suite) => {
45+
if (suite.root) {
46+
return
47+
}
48+
console.log(`Suite '${suite.title}' finished`)
49+
console.log()
50+
})
51+
52+
runner.on(Mocha.Runner.constants.EVENT_TEST_BEGIN, (test) => {
53+
console.log(`Starting test '${test.title}'`)
54+
})
55+
56+
runner.on(Mocha.Runner.constants.EVENT_TEST_PASS, (test) => {
57+
console.log(`Test '${test.title}' passed in ${test.duration} ms`)
58+
})
59+
60+
runner.on(Mocha.Runner.constants.EVENT_TEST_PENDING, (test) => {
61+
console.log(`Test '${test.title}' skipped in ${test.duration} ms`)
62+
})
63+
64+
runner.on(Mocha.Runner.constants.EVENT_TEST_FAIL, (test, error) => {
65+
this.failedTests.push({ test, error })
66+
console.log(`Test '${test.title}' failed in ${test.duration} ms with ${error}`)
67+
})
68+
69+
runner.on(Mocha.Runner.constants.EVENT_TEST_END, (test) => {
70+
console.log()
71+
})
72+
}
73+
}
74+
75+
module.exports = Reporter

test/test-addon.js

+101-99
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

3-
const test = require('tap').test
3+
const { describe, it } = require('mocha')
4+
const assert = require('assert')
45
const path = require('path')
56
const fs = require('graceful-fs')
67
const childProcess = require('child_process')
@@ -35,116 +36,117 @@ function checkCharmapValid () {
3536
return lines.pop() === 'True'
3637
}
3738

38-
test('build simple addon', function (t) {
39-
t.plan(3)
40-
41-
// Set the loglevel otherwise the output disappears when run via 'npm test'
42-
var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose']
43-
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
44-
var logLines = stderr.toString().trim().split(/\r?\n/)
45-
var lastLine = logLines[logLines.length - 1]
46-
t.strictEqual(err, null)
47-
t.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
48-
t.strictEqual(runHello().trim(), 'world')
39+
describe('addon', function () {
40+
this.timeout(300000)
41+
42+
it('build simple addon', function (done) {
43+
// Set the loglevel otherwise the output disappears when run via 'npm test'
44+
var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose']
45+
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
46+
var logLines = stderr.toString().trim().split(/\r?\n/)
47+
var lastLine = logLines[logLines.length - 1]
48+
assert.strictEqual(err, null)
49+
assert.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
50+
assert.strictEqual(runHello().trim(), 'world')
51+
done()
52+
})
53+
proc.stdout.setEncoding('utf-8')
54+
proc.stderr.setEncoding('utf-8')
4955
})
50-
proc.stdout.setEncoding('utf-8')
51-
proc.stderr.setEncoding('utf-8')
52-
})
53-
54-
test('build simple addon in path with non-ascii characters', function (t) {
55-
t.plan(1)
5656

57-
if (!checkCharmapValid()) {
58-
return t.skip('python console app can\'t encode non-ascii character.')
59-
}
57+
it('build simple addon in path with non-ascii characters', function (done) {
58+
if (!checkCharmapValid()) {
59+
return this.skip('python console app can\'t encode non-ascii character.')
60+
}
6061

61-
var testDirNames = {
62-
cp936: '文件夹',
63-
cp1252: 'Latīna',
64-
cp932: 'フォルダ'
65-
}
66-
// Select non-ascii characters by current encoding
67-
var testDirName = testDirNames[getEncoding()]
68-
// If encoding is UTF-8 or other then no need to test
69-
if (!testDirName) {
70-
return t.skip('no need to test')
71-
}
62+
var testDirNames = {
63+
cp936: '文件夹',
64+
cp1252: 'Latīna',
65+
cp932: 'フォルダ'
66+
}
67+
// Select non-ascii characters by current encoding
68+
var testDirName = testDirNames[getEncoding()]
69+
// If encoding is UTF-8 or other then no need to test
70+
if (!testDirName) {
71+
return this.skip('no need to test')
72+
}
7273

73-
t.plan(3)
74+
this.timeout(300000)
7475

75-
var data
76-
var configPath = path.join(addonPath, 'build', 'config.gypi')
77-
try {
78-
data = fs.readFileSync(configPath, 'utf8')
79-
} catch (err) {
80-
t.error(err)
81-
return
82-
}
83-
var config = JSON.parse(data.replace(/#.+\n/, ''))
84-
var nodeDir = config.variables.nodedir
85-
var testNodeDir = path.join(addonPath, testDirName)
86-
// Create symbol link to path with non-ascii characters
87-
try {
88-
fs.symlinkSync(nodeDir, testNodeDir, 'dir')
89-
} catch (err) {
90-
switch (err.code) {
91-
case 'EEXIST': break
92-
case 'EPERM':
93-
t.error(err, 'Please try to running console as an administrator')
94-
return
95-
default:
96-
t.error(err)
97-
return
76+
var data
77+
var configPath = path.join(addonPath, 'build', 'config.gypi')
78+
try {
79+
data = fs.readFileSync(configPath, 'utf8')
80+
} catch (err) {
81+
assert.fail(err)
82+
return
9883
}
99-
}
100-
101-
var cmd = [
102-
nodeGyp,
103-
'rebuild',
104-
'-C',
105-
addonPath,
106-
'--loglevel=verbose',
107-
'-nodedir=' + testNodeDir
108-
]
109-
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
84+
var config = JSON.parse(data.replace(/#.+\n/, ''))
85+
var nodeDir = config.variables.nodedir
86+
var testNodeDir = path.join(addonPath, testDirName)
87+
// Create symbol link to path with non-ascii characters
11088
try {
111-
fs.unlink(testNodeDir)
89+
fs.symlinkSync(nodeDir, testNodeDir, 'dir')
11290
} catch (err) {
113-
t.error(err)
91+
switch (err.code) {
92+
case 'EEXIST': break
93+
case 'EPERM':
94+
assert.fail(err, null, 'Please try to running console as an administrator')
95+
return
96+
default:
97+
assert.fail(err)
98+
return
99+
}
114100
}
115101

116-
var logLines = stderr.toString().trim().split(/\r?\n/)
117-
var lastLine = logLines[logLines.length - 1]
118-
t.strictEqual(err, null)
119-
t.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
120-
t.strictEqual(runHello().trim(), 'world')
102+
var cmd = [
103+
nodeGyp,
104+
'rebuild',
105+
'-C',
106+
addonPath,
107+
'--loglevel=verbose',
108+
'-nodedir=' + testNodeDir
109+
]
110+
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
111+
try {
112+
fs.unlink(testNodeDir)
113+
} catch (err) {
114+
assert.fail(err)
115+
}
116+
117+
var logLines = stderr.toString().trim().split(/\r?\n/)
118+
var lastLine = logLines[logLines.length - 1]
119+
assert.strictEqual(err, null)
120+
assert.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
121+
assert.strictEqual(runHello().trim(), 'world')
122+
done()
123+
})
124+
proc.stdout.setEncoding('utf-8')
125+
proc.stderr.setEncoding('utf-8')
121126
})
122-
proc.stdout.setEncoding('utf-8')
123-
proc.stderr.setEncoding('utf-8')
124-
})
125-
126-
test('addon works with renamed host executable', function (t) {
127-
// No `fs.copyFileSync` before node8.
128-
if (process.version.substr(1).split('.')[0] < 8) {
129-
t.skip('skipping test for old node version')
130-
t.end()
131-
return
132-
}
133127

134-
t.plan(3)
135-
136-
var notNodePath = path.join(os.tmpdir(), 'notnode' + path.extname(process.execPath))
137-
fs.copyFileSync(process.execPath, notNodePath)
128+
it('addon works with renamed host executable', function (done) {
129+
// No `fs.copyFileSync` before node8.
130+
if (process.version.substr(1).split('.')[0] < 8) {
131+
return this.skip('skipping test for old node version')
132+
}
138133

139-
var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose']
140-
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
141-
var logLines = stderr.toString().trim().split(/\r?\n/)
142-
var lastLine = logLines[logLines.length - 1]
143-
t.strictEqual(err, null)
144-
t.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
145-
t.strictEqual(runHello(notNodePath).trim(), 'world')
146-
fs.unlinkSync(notNodePath)
134+
this.timeout(300000)
135+
136+
var notNodePath = path.join(os.tmpdir(), 'notnode' + path.extname(process.execPath))
137+
fs.copyFileSync(process.execPath, notNodePath)
138+
139+
var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose']
140+
var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) {
141+
var logLines = stderr.toString().trim().split(/\r?\n/)
142+
var lastLine = logLines[logLines.length - 1]
143+
assert.strictEqual(err, null)
144+
assert.strictEqual(lastLine, 'gyp info ok', 'should end in ok')
145+
assert.strictEqual(runHello(notNodePath).trim(), 'world')
146+
fs.unlinkSync(notNodePath)
147+
done()
148+
})
149+
proc.stdout.setEncoding('utf-8')
150+
proc.stderr.setEncoding('utf-8')
147151
})
148-
proc.stdout.setEncoding('utf-8')
149-
proc.stderr.setEncoding('utf-8')
150152
})

0 commit comments

Comments
 (0)