UNPKG

parser-mantester

Version:

ManTester.com Parser from ManTester-enhanced Markdown to JSON data structure.

168 lines (151 loc) 5.49 kB
import { unified } from 'unified' import remarkParse from 'remark-parse' import remarkHtml from 'remark-html' const htmlHolder = {} remarkHtml.call(htmlHolder, {}) const toHtml = ast => htmlHolder.Compiler(ast, {}) const debug = false const processor = unified() .use(remarkParse) const isTestCaseStart = node => node.type === 'heading' && node.depth === 2 const isTestCaseEnd = node => !node || (node.type === 'heading' && node.depth <= 2) const isTestStepsStart = node => node.type === 'heading' && node.depth === 3 const isTestStepsEnd = node => !node || node.type === 'heading' const isList = node => node.type === 'list' const isInTestCase = status => status === 'testCase' const isInTestSteps = status => status === 'testSteps' const extractTextDescendants = node => { if (node.type === 'text') { return [node] } if (node.children) { const arrays = node.children .map(extractTextDescendants) return [].concat(...arrays) } return [] } const extractTextFromDescendants = textDescendants => squashWhitespaces( textDescendants .map(({ value }) => value) .join(' ') .trim() ) const squashWhitespaces = string => string.replace(/[ \t]+/g, ' ') const groupRegex = /@(\w+)/g const getGroups = text => [...text.matchAll(groupRegex)] .map(([, group]) => group) const categoryRegex = /!(\w+)/g const getCategory = text => [...text.matchAll(categoryRegex)] .map(([, group]) => group)[0] const removeEmptyParagraphs = string => string.replace(/<p>\s*<\/p>/g, '') const resultRegex = /\s\|\s([^|]+)$/g const getResult = text => { const matches = [...text.matchAll(resultRegex)] .map(([, result]) => result) if (matches.length) { return matches[matches.length - 1] } } export const parse = markdown => { const ast = processor().parse(markdown) // debug && console.log('index.js AST', JSON.stringify(ast, null, 2)) // debug && console.log('index.js HTML', toHtml(ast)) let status = 'outside' const testCases = [] let testCase const close = node => { if (isInTestSteps(status) && isTestStepsEnd(node)) { status = 'testCase' } if (isInTestCase(status) && isTestCaseEnd(node)) { testCases.push(testCase) debug && console.log('index.js closed test case', testCase) } } for (const node of ast.children) { // debug && console.log('index.js parse', status, JSON.stringify(node, null, 2)) close(node) if (isTestCaseStart(node)) { status = 'testCase' const code = node.children .filter(({ type }) => type === 'inlineCode') .map(({ value }) => value) .join('') .trim() const name = extractTextFromDescendants(extractTextDescendants(node)) testCase = { code, name, category: null, groups: [], description: null, steps: [] } debug && console.log('index.js started test case', { code, name }) } if (!isTestCaseStart(node) && isInTestCase(status)) { const textDescendants = extractTextDescendants(node) const text = extractTextFromDescendants(textDescendants) const groups = getGroups(text) // debug && console.log('index.js finding groups and category in:', text) if (groups.length) { if (!testCase.groups) { testCase.groups = [] } testCase.groups.push(...groups) textDescendants.forEach(node => { node.value = node.value.replace(groupRegex, '') }) debug && console.log('index.js detected groups', groups) } const category = getCategory(text) if (category) { testCase.category = category textDescendants.forEach(node => { node.value = node.value.replace(categoryRegex, '') }) debug && console.log('index.js detected category', category) } } if ((!isTestCaseStart(node) && isInTestCase(status) && !isTestStepsStart(node)) || (isInTestSteps(status) && !isList(node))) { if (!testCase.description) { testCase.description = '' } const description = squashWhitespaces(removeEmptyParagraphs(toHtml(node))) testCase.description += description testCase.description = testCase.description || null debug && console.log('index.js description', description) } if (isInTestCase(status) && isTestStepsStart(node)) { status = 'testSteps' debug && console.log('index.js started test steps') } if (isInTestSteps(status) && isList(node)) { const results = [] node.children.forEach(step => { const textDescendants = extractTextDescendants(step) const resultDescendantIndex = textDescendants .map(({ value }) => !!getResult(value)) .lastIndexOf(true) if (resultDescendantIndex >= 0) { const descendant = textDescendants[resultDescendantIndex] results.push(getResult(descendant.value)) descendant.value = descendant.value.replace(resultRegex, '') } else { results.push(undefined) } }) const testSteps = node.children .map(toHtml) .map(string => string.substring('<li>'.length, string.length - '</li>'.length)) .map((task, index) => ({ task, result: results[index] })) testCase.steps.push(...testSteps) debug && console.log('index.js testSteps', testSteps) } } close() return { testCases } } if (typeof window !== 'undefined') { window.ManTesterParser = { parse } }