Skip to content

Commit

Permalink
feat: mocha framework included by default
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-redFox committed May 6, 2024
1 parent 59b1e0b commit 3a991b9
Show file tree
Hide file tree
Showing 13 changed files with 356 additions and 131 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ use the awesome [Socket.io] library and [Node.js].
[Socket.io]: https://socket.io/
[Node.js]: https://nodejs.org/
[Jasmine]: https://github.com/karma-runner/karma-jasmine
[Mocha]: https://github.com/karma-runner/karma-mocha
[QUnit]: https://github.com/karma-runner/karma-qunit
[here]: https://www.youtube.com/watch?v=MVw8N3hTfCI
[Mailing List]: https://groups.google.com/forum/#!forum/karma-users
Expand Down
1 change: 0 additions & 1 deletion docs/config/05-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ The recommended way to install plugins is to add them as project dependencies in
{
"devDependencies": {
"karma": "~0.10",
"karma-mocha": "~0.0.1",
"karma-growl-reporter": "~0.0.1",
"karma-firefox-launcher": "~0.0.1"
}
Expand Down
5 changes: 3 additions & 2 deletions docs/dev/05-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ This section outlines API requirements and conventions for different plugin type

### Frameworks

- example plugins: [karma-jasmine], [karma-mocha], [karma-requirejs]
Mocha JS already included by default and you don't need additinal package for that

- example plugins: [karma-jasmine], [karma-requirejs]
- use naming convention is `karma-*`
- use npm keywords `karma-plugin`, `karma-framework`.

Expand Down Expand Up @@ -174,7 +176,6 @@ A preprocessor is a function that accepts three arguments (`content`, `file`, an
As Karma is assembled by dependency injection, a plugin can ask for pretty much any Karma component and interact with it. There are a couple of plugins that do more interesting stuff like this, check out [karma-closure], [karma-intellij].

[karma-jasmine]: https://github.com/karma-runner/karma-jasmine
[karma-mocha]: https://github.com/karma-runner/karma-mocha
[karma-requirejs]: https://github.com/karma-runner/karma-requirejs
[karma-growl-reporter]: https://github.com/karma-runner/karma-growl-reporter
[karma-junit-reporter]: https://github.com/karma-runner/karma-junit-reporter
Expand Down
213 changes: 213 additions & 0 deletions lib/frameworks/mocha-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
const formatError = function (error) {
let stack = error.stack
const message = error.message

if (stack) {
if (message && !stack.includes(message)) {
stack = message + '\n' + stack
}

// remove mocha stack entries
return stack.replace(/\n.+\/mocha\/mocha\.js\?\w*:[\d:]+\)?(?=(\n|$))/g, '')
}

return message
}

const processAssertionError = function (error_) {
let error

if (window.Mocha && Object.hasOwn(error_, 'showDiff')) {
error = {
name: error_.name,
message: error_.message,
showDiff: error_.showDiff
}

if (error.showDiff) {
error.actual = window.Mocha.utils.stringify(error_.actual)
error.expected = window.Mocha.utils.stringify(error_.expected)
}
}

return error
}

const createMochaReporterNode = function () {
const mochaRunnerNode = document.createElement('div')
mochaRunnerNode.setAttribute('id', 'mocha')
document.body.appendChild(mochaRunnerNode)
}

const haveMochaConfig = function (karma) {
return karma.config?.mocha
}

const reportTestResult = function (karma, test) {
const skipped = test.pending === true

const result = {
id: '',
description: test.title,
suite: [],
success: test.state === 'passed',
skipped,
pending: skipped,
time: skipped ? 0 : test.duration,
log: test.$errors || [],
assertionErrors: test.$assertionErrors || [],
startTime: test.$startTime,
endTime: Date.now()
}

let pointer = test.parent
while (!pointer.root) {
result.suite.unshift(pointer.title)
pointer = pointer.parent
}

if (haveMochaConfig(karma) && karma.config?.mocha?.expose?.forEach) {
result.mocha = {}
karma.config.mocha.expose.forEach((prop) => {
if (Object.hasOwn(test, prop)) {
result.mocha[prop] = test[prop]
}
})
}

karma.result(result)
}

const createMochaReporterConstructor = function (tc, pathname) {
const isDebugPage = /debug.html$/.test(pathname)

// Set custom reporter on debug page
if (isDebugPage && haveMochaConfig(tc) && tc.config.mocha.reporter) {
createMochaReporterNode()
return tc.config.mocha.reporter
}

// TODO(vojta): error formatting
return function (runner) {
// runner events
// - start
// - end
// - suite
// - suite end
// - test
// - test end
// - pass
// - fail
// - pending

runner.on('start', function () {
tc.info({ total: runner.total })
})

runner.on('end', function () {
tc.complete({
coverage: window.__coverage__
})
})

runner.on('test', function (test) {
test.$startTime = Date.now()
test.$errors = []
test.$assertionErrors = []
})

runner.on('pending', function (test) {
test.pending = true
})

runner.on('fail', function (test, error) {
const simpleError = formatError(error)
const assertionError = processAssertionError(error)

if (test.type === 'hook') {
test.$errors = isDebugPage ? [error] : [simpleError]
test.$assertionErrors = assertionError ? [assertionError] : []
reportTestResult(tc, test)
} else {
test.$errors.push(isDebugPage ? error : simpleError)
if (assertionError) test.$assertionErrors.push(assertionError)
}
})

runner.on('test end', function (test) {
reportTestResult(tc, test)
})
}
}

const createMochaStartFn = function (mocha) {
return function (config = {}) {
const clientArguments = config.args

if (clientArguments) {
if (Array.isArray(clientArguments)) {
clientArguments.reduce((isGrepArg, arg) => {
if (isGrepArg) {
mocha.grep(new RegExp(arg))
} else if (arg === '--grep') {
return true
} else {
const match = /--grep=(.*)/.exec(arg)

if (match) {
mocha.grep(new RegExp(match[1]))
}
}
return false
}, false)
}

/**
* TODO(maksimrv): remove when karma-grunt plugin will pass
* clientArguments how Array
*/
if (clientArguments.grep) {
mocha.grep(clientArguments.grep)
}
}

mocha.run()
}
}

// Default configuration
const mochaConfig = {
reporter: createMochaReporterConstructor(
window.__karma__,
window.location.pathname
),
ui: 'bdd',
globals: ['__cov*']
}

// Pass options from client.mocha to mocha
const createConfigObject = function (karma) {
if (!karma.config?.mocha) {
return mochaConfig
}

// Copy all properties to mochaConfig
for (const key in karma.config.mocha) {
// except for reporter, require, or expose
if (['reporter', 'require', 'expose'].includes(key)) {
continue
}

// and merge the globals if they exist.
if (key === 'globals') {
mochaConfig.globals = mochaConfig.globals.concat(karma.config.mocha[key])
continue
}

mochaConfig[key] = karma.config.mocha[key]
}
return mochaConfig
}

window.__karma__.start = createMochaStartFn(window.mocha)
window.mocha.setup(createConfigObject(window.__karma__))
29 changes: 29 additions & 0 deletions lib/frameworks/mocha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const path = require('path')

const createPattern = function (path) {
return { pattern: path, included: true, served: true, watched: false }
}

const initMocha = function (files, config) {
const mochaPath = path.dirname(require.resolve('mocha'))
files.unshift(createPattern(path.join(__dirname, 'mocha-adapter.js')))

const mochaConfig = config?.client?.mocha
if (mochaConfig?.require) {
mochaConfig.require.forEach((requirePath) =>
files.unshift(createPattern(requirePath))
)
}

files.unshift(createPattern(path.join(mochaPath, 'mocha.js')))

if (mochaConfig?.reporter) {
files.unshift(createPattern(path.join(mochaPath, 'mocha.css')))
}
}

initMocha.$inject = ['config.files', 'config']

module.exports = {
'framework:mocha': ['factory', initMocha]
}
5 changes: 3 additions & 2 deletions lib/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict'

const createSocketIoServer = require('./ws')
const di = require('di')
const util = require('util')
const spawn = require('child_process').spawn
Expand All @@ -16,6 +15,8 @@ const logger = require('./logger')
const constant = require('./constants')
const watcher = require('./watcher')
const plugin = require('./plugin')
const createSocketIoServer = require('./ws')
const mocha = require('./frameworks/mocha')

const createServeFile = require('./web-server').createServeFile
const createServeStaticFile = require('./web-server').createServeStaticFile
Expand Down Expand Up @@ -79,7 +80,7 @@ class Server extends KarmaEventEmitter {
},
clearTimeout
}]
}]
}, mocha]

this.on('load_error', (type, name) => {
this.log.debug(`Registered a load error of type ${type} with name ${name}`)
Expand Down
Loading

0 comments on commit 3a991b9

Please sign in to comment.