Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mocha framework included by default #10

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading