codeceptjs
Version:
Supercharged End 2 End Testing Framework for NodeJS
141 lines (123 loc) • 4.27 kB
JavaScript
import recorder from './recorder.js'
import container from './container.js'
import event from './event.js'
import output from './output.js'
import store from './store.js'
import { isAsyncFunction } from './utils.js'
const sessionColors = ['cyan', 'blue', 'red', 'magenta', 'yellow']
const savedSessions = {}
/**
* @param {CodeceptJS.LocatorOrString} sessionName
* @param {Function | Object<string, *>} config
* @param {Function} [fn]
* @return {any}
*/
function session(sessionName, config, fn) {
if (typeof config === 'function') {
if (typeof fn === 'function') {
config = config()
} else {
// no config but with function
fn = config
config = {}
}
}
// session helpers won't use beforeSuite and afterSuite hooks...
// restart: false options are not allowed as well
// but those helpers should be already started so inside listener/helpers.js the `_init` method should already be called
const helpers = container.helpers()
if (!savedSessions[sessionName]) {
for (const helper in helpers) {
if (!helpers[helper]._session) continue
savedSessions[sessionName] = {
start: () => null,
stop: () => null,
loadVars: () => null,
restoreVars: () => null,
...(store.dryRun ? {} : helpers[helper]._session()),
}
break
}
const closeBrowsers = () => {
const session = savedSessions[sessionName]
if (!session) return
session.stop(session.vars)
delete savedSessions[sessionName]
}
event.dispatcher.once(event.test.after, () => {
recorder.add('close session browsers', closeBrowsers)
})
if (!savedSessions[sessionName]) {
throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
}
recorder.add('save vars', async () => {
savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
})
}
// pick random color
const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
const addContextToStep = step => {
step.actor = `${output.colors[color](sessionName)}: I`
}
if (!fn) return // no current session steps
return recorder.add(
'register session wrapper',
async () => {
const session = savedSessions[sessionName]
if (!session) {
throw new Error(`Session "${sessionName}" not found. It may have been closed already.`)
}
recorder.session.start(`session:${sessionName}`)
event.dispatcher.on(event.step.after, addContextToStep)
recorder.add('switch to browser', () => {
const session = savedSessions[sessionName]
return session.loadVars(session.vars)
})
const finalize = () => {
recorder.add('Finalize session', async () => {
output.stepShift = 0
event.dispatcher.removeListener(event.step.after, addContextToStep)
await session.restoreVars()
recorder.session.restore(`session:${sessionName}`)
})
}
// Indicate when executing this function that we are in a session
if (isAsyncFunction(fn)) {
return fn
.apply(null)
.then(async res => {
finalize()
await recorder.promise()
return res
})
.catch(e => {
output.stepShift = 0
session.restoreVars(sessionName)
event.dispatcher.removeListener(event.step.after, addContextToStep)
recorder.add('restore session on error', () => recorder.session.restore(`session:${sessionName}`))
recorder.throw(e)
return recorder.promise()
})
}
let res
try {
res = fn.apply(null)
} catch (err) {
recorder.throw(err)
} finally {
recorder.catch(e => {
session.restoreVars(sessionName)
output.stepShift = 0
event.dispatcher.removeListener(event.step.after, addContextToStep)
recorder.session.restore(`session:${sessionName}`)
throw e
})
}
finalize()
return recorder.promise().then(() => res)
},
false,
false,
)
}
export default session