codeceptjs
Version:
Supercharged End 2 End Testing Framework for NodeJS
136 lines (120 loc) • 4.64 kB
JavaScript
import escapeStringRegexp from 'escape-string-regexp'
import fs from 'fs'
import Gherkin from '@cucumber/gherkin'
import { IdGenerator } from '@cucumber/messages'
import { globSync } from 'glob'
import fsPath from 'path'
import { getConfig, getTestRoot } from '../utils.js'
import Codecept from '../../codecept.js'
import output from '../../output.js'
import store from '../../store.js'
import { matchStep } from '../../mocha/bdd.js'
const uuidFn = IdGenerator.uuid()
const builder = new Gherkin.AstBuilder(uuidFn)
const matcher = new Gherkin.GherkinClassicTokenMatcher()
const parser = new Gherkin.Parser(builder, matcher)
parser.stopAtFirstError = false
export default async function (genPath, options) {
const configFile = options.config || genPath
const testsPath = getTestRoot(configFile)
const config = await getConfig(configFile)
if (!config) return
const codecept = new Codecept(config, {})
await codecept.init(testsPath)
if (!config.gherkin) {
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it')
process.exit(1)
}
if (!config.gherkin.steps || !config.gherkin.steps[0]) {
output.error('No gherkin steps defined in config. Exiting')
process.exit(1)
}
if (!options.feature && !config.gherkin.features) {
output.error('No gherkin features defined in config. Exiting')
process.exit(1)
}
if (options.path && !config.gherkin.steps.includes(options.path)) {
output.error(`You must include ${options.path} to the gherkin steps in your config file`)
process.exit(1)
}
const files = []
globSync(options.feature || config.gherkin.features, { cwd: options.feature ? '.' : store.codeceptDir }).forEach(file => {
if (!fsPath.isAbsolute(file)) {
file = fsPath.join(store.codeceptDir, file)
}
files.push(fsPath.resolve(file))
})
output.print(`Loaded ${files.length} files`)
const newSteps = new Map()
const parseSteps = steps => {
const newSteps = []
let currentKeyword = ''
for (const step of steps) {
if (step.keyword.trim() === 'And') {
if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`)
step.keyword = currentKeyword
}
currentKeyword = step.keyword
try {
matchStep(step.text)
} catch (err) {
let stepLine
if (/[{}()/]/.test(step.text)) {
stepLine = escapeStringRegexp(step.text)
.replace(/\//g, '\\/')
.replace(/\"(.*?)\"/g, '"(.*?)"')
.replace(/(\d+\\\.\d+)/, '(\\d+\\.\\d+)')
.replace(/ (\d+) /, ' (\\d+) ')
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: true })
} else {
stepLine = step.text
.replace(/\"(.*?)\"/g, '{string}')
.replace(/(\d+\.\d+)/, '{float}')
.replace(/ (\d+) /, ' {int} ')
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: false })
}
newSteps.push(stepLine)
}
}
return newSteps
}
const parseFile = file => {
const ast = parser.parse(fs.readFileSync(file).toString())
for (const child of ast.feature.children) {
if (child.scenario.keyword === 'Scenario Outline') continue // skip scenario outline
parseSteps(child.scenario.steps)
.map(step => {
return Object.assign(step, { file: file.replace(store.codeceptDir, '').slice(1) })
})
.map(step => newSteps.set(`${step.type}(${step})`, step))
}
}
files.forEach(file => parseFile(file))
let stepFile = options.path || config.gherkin.steps[0]
if (!fs.existsSync(stepFile)) {
output.error(`Please enter a valid step file path ${stepFile}`)
process.exit(1)
}
if (!fsPath.isAbsolute(stepFile)) {
stepFile = fsPath.join(store.codeceptDir, stepFile)
}
const snippets = [...newSteps.values()]
.filter((value, index, self) => self.indexOf(value) === index)
.map(step => {
return `
${step.type}(${step.regexp ? '/^' : "'"}${step}${step.regexp ? '$/' : "'"}, () => {
// From "${step.file}" ${JSON.stringify(step.location)}
throw new Error('Not implemented yet');
});`
})
if (!snippets.length) {
output.print('No new snippets found')
return
}
output.success(`Snippets generated: ${snippets.length}`)
output.print(snippets.join('\n'))
if (!options.dryRun) {
output.success(`Snippets added to ${output.colors.bold(stepFile)}`)
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n')
}
}