@cucumber/gherkin-utils
Advanced tools
Comparing version 6.0.0 to 7.0.0
@@ -0,1 +1,3 @@ | ||
export * from './walkGherkinDocument'; | ||
export * from './GherkinDocumentHandlers'; | ||
import pretty from './pretty'; | ||
@@ -2,0 +4,0 @@ import Query from './Query'; |
@@ -14,2 +14,5 @@ "use strict"; | ||
}); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
@@ -27,2 +30,4 @@ if (mod && mod.__esModule) return mod; | ||
exports.rejectAllFilters = exports.GherkinDocumentWalker = exports.Query = exports.pretty = void 0; | ||
__exportStar(require("./walkGherkinDocument"), exports); | ||
__exportStar(require("./GherkinDocumentHandlers"), exports); | ||
const pretty_1 = __importDefault(require("./pretty")); | ||
@@ -29,0 +34,0 @@ exports.pretty = pretty_1.default; |
import * as messages from '@cucumber/messages'; | ||
export default function pretty(gherkinDocument: messages.GherkinDocument): string; | ||
export declare type Syntax = 'markdown' | 'gherkin'; | ||
export default function pretty(gherkinDocument: messages.GherkinDocument, syntax?: Syntax): string; | ||
export declare function escapeCell(s: string): string; | ||
//# sourceMappingURL=pretty.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function pretty(gherkinDocument) { | ||
const feature = gherkinDocument.feature; | ||
let s = prettyTags(feature.tags); | ||
s += feature.keyword + ': ' + feature.name + '\n'; | ||
if (feature.description) { | ||
s += feature.description + '\n'; | ||
} | ||
for (const child of feature.children) { | ||
if (child.background) { | ||
s += prettyStepContainer(child.background, ' '); | ||
} | ||
else if (child.scenario) { | ||
s += prettyStepContainer(child.scenario, ' '); | ||
} | ||
else if (child.rule) { | ||
s += `\n ${child.rule.keyword}: ${child.rule.name}\n`; | ||
if (child.rule.description) { | ||
s += child.rule.description + '\n'; | ||
} | ||
for (const ruleChild of child.rule.children) { | ||
if (ruleChild.background) { | ||
s += prettyStepContainer(ruleChild.background, ' '); | ||
exports.escapeCell = void 0; | ||
const walkGherkinDocument_1 = require("./walkGherkinDocument"); | ||
function pretty(gherkinDocument, syntax = 'gherkin') { | ||
let scenarioLevel = 1; | ||
return (0, walkGherkinDocument_1.walkGherkinDocument)(gherkinDocument, '', { | ||
feature(feature, content) { | ||
return content | ||
.concat(prettyLanguageHeader(feature.language)) | ||
.concat(prettyKeywordContainer(feature, syntax, 0)); | ||
}, | ||
rule(rule, content) { | ||
scenarioLevel = 2; | ||
return content.concat(prettyKeywordContainer(rule, syntax, 1)); | ||
}, | ||
background(background, content) { | ||
return content.concat(prettyKeywordContainer(background, syntax, scenarioLevel)); | ||
}, | ||
scenario(scenario, content) { | ||
return content.concat(prettyKeywordContainer(scenario, syntax, scenarioLevel)); | ||
}, | ||
examples(examples, content) { | ||
const tableRows = examples.tableHeader ? [examples.tableHeader, ...examples.tableBody] : []; | ||
return content | ||
.concat(prettyKeywordContainer(examples, syntax, scenarioLevel + 1)) | ||
.concat(prettyTableRows(tableRows, syntax, scenarioLevel + 2)); | ||
}, | ||
step(step, content) { | ||
return content | ||
.concat(stepPrefix(scenarioLevel + 1, syntax)) | ||
.concat(step.keyword) | ||
.concat(step.text) | ||
.concat('\n'); | ||
}, | ||
dataTable(dataTable, content) { | ||
const level = syntax === 'markdown' ? 1 : scenarioLevel + 2; | ||
return content.concat(prettyTableRows(dataTable.rows || [], syntax, level)); | ||
}, | ||
docString(docString, content) { | ||
const delimiter = makeDocStringDelimiter(syntax, docString); | ||
const level = syntax === 'markdown' ? 1 : scenarioLevel + 2; | ||
const indent = spaces(level); | ||
let docStringContent = docString.content.replace(/^/gm, indent); | ||
if (syntax === 'gherkin') { | ||
if (docString.delimiter === '"""') { | ||
docStringContent = docStringContent.replace(/"""/gm, '\\"\\"\\"'); | ||
} | ||
if (ruleChild.scenario) { | ||
s += prettyStepContainer(ruleChild.scenario, ' '); | ||
else { | ||
docStringContent = docStringContent.replace(/```/gm, '\\`\\`\\`'); | ||
} | ||
} | ||
} | ||
} | ||
return s; | ||
return content | ||
.concat(indent) | ||
.concat(delimiter) | ||
.concat(docString.mediaType || '') | ||
.concat('\n') | ||
.concat(docStringContent) | ||
.concat('\n') | ||
.concat(indent) | ||
.concat(delimiter) | ||
.concat('\n'); | ||
}, | ||
}); | ||
} | ||
exports.default = pretty; | ||
function prettyStepContainer(stepContainer, indent) { | ||
const scenario = 'tags' in stepContainer ? stepContainer : null; | ||
const tags = (scenario === null || scenario === void 0 ? void 0 : scenario.tags) || []; | ||
let s = `\n${prettyTags(tags, indent)}${indent}${stepContainer.keyword}: ${stepContainer.name}\n`; | ||
if (stepContainer.description) { | ||
s += stepContainer.description + '\n\n'; | ||
function prettyLanguageHeader(language) { | ||
return language === 'en' ? '' : `# language: ${language}\n`; | ||
} | ||
function prettyKeywordContainer(stepContainer, syntax, level) { | ||
const tags = 'tags' in stepContainer ? stepContainer.tags : []; | ||
const stepCount = 'steps' in stepContainer ? stepContainer.steps.length : 0; | ||
const description = prettyDescription(stepContainer.description, syntax); | ||
return '' | ||
.concat(level === 0 ? '' : '\n') | ||
.concat(prettyTags(tags, syntax, level)) | ||
.concat(keywordPrefix(level, syntax)) | ||
.concat(stepContainer.keyword) | ||
.concat(': ') | ||
.concat(stepContainer.name) | ||
.concat('\n') | ||
.concat(description) | ||
.concat(description && stepCount > 0 ? '\n' : ''); | ||
} | ||
function prettyDescription(description, syntax) { | ||
if (!description) | ||
return ''; | ||
if (syntax === 'gherkin') | ||
return description + '\n'; | ||
else | ||
return description.replace(/^\s*/gm, '') + '\n'; | ||
} | ||
function prettyTags(tags, syntax, level) { | ||
if (tags === undefined || tags.length == 0) { | ||
return ''; | ||
} | ||
for (const step of stepContainer.steps) { | ||
s += `${indent} ${step.keyword}${step.text}\n`; | ||
const prefix = syntax === 'gherkin' ? spaces(level) : ''; | ||
const tagQuote = syntax === 'gherkin' ? '' : '`'; | ||
return prefix + tags.map((tag) => `${tagQuote}${tag.name}${tagQuote}`).join(' ') + '\n'; | ||
} | ||
function keywordPrefix(level, syntax) { | ||
if (syntax === 'markdown') { | ||
return new Array(level + 2).join('#') + ' '; | ||
} | ||
if (scenario) { | ||
for (const example of scenario.examples) { | ||
s += prettyExample(example, `${indent} `); | ||
} | ||
else { | ||
return spaces(level); | ||
} | ||
return s; | ||
} | ||
function prettyExample(example, indent) { | ||
let s = `\n${indent}Examples: ${example.name}\n`; | ||
s += prettyTableRow(example.tableHeader, `${indent} `); | ||
for (const row of example.tableBody) { | ||
s += prettyTableRow(row, `${indent} `); | ||
function stepPrefix(level, syntax) { | ||
if (syntax === 'markdown') { | ||
return '* '; | ||
} | ||
return s; | ||
else { | ||
return new Array(level + 1).join(' '); | ||
} | ||
} | ||
function prettyTableRow(row, indent) { | ||
return `${indent}| ${row.cells.map((cell) => cell.value).join(' | ')} |\n`; | ||
function spaces(level) { | ||
return new Array(level + 1).join(' '); | ||
} | ||
function prettyTags(tags, indent = '') { | ||
if (tags === undefined || tags.length == 0) { | ||
function makeDocStringDelimiter(syntax, docString) { | ||
if (syntax === 'gherkin') { | ||
return docString.delimiter.substring(0, 3); | ||
} | ||
// The length of the fenced code block delimiter is three backticks when the content inside doesn't have backticks. | ||
// If the content inside has three or more backticks, the number of backticks in the delimiter must be at least one more | ||
// https://github.github.com/gfm/#fenced-code-blocks | ||
const threeOrMoreBackticks = /(```+)/g; | ||
let maxContentBackTickCount = 2; | ||
let match; | ||
do { | ||
match = threeOrMoreBackticks.exec(docString.content); | ||
if (match) { | ||
maxContentBackTickCount = Math.max(maxContentBackTickCount, match[1].length); | ||
} | ||
} while (match); | ||
// Return a delimiter with one more backtick than the max number of backticks in the contents (3 ny default) | ||
return new Array(maxContentBackTickCount + 2).join('`'); | ||
} | ||
function prettyTableRows(tableRows, syntax, level) { | ||
if (tableRows.length === 0) | ||
return ''; | ||
const maxWidths = new Array(tableRows[0].cells.length).fill(0); | ||
tableRows.forEach((tableRow) => { | ||
tableRow.cells.forEach((tableCell, j) => { | ||
maxWidths[j] = Math.max(maxWidths[j], escapeCell(tableCell.value).length); | ||
}); | ||
}); | ||
let n = 0; | ||
let s = ''; | ||
for (const row of tableRows) { | ||
s += prettyTableRow(row, level, maxWidths, syntax); | ||
if (n === 0 && syntax === 'markdown') { | ||
const separatorRow = { | ||
location: row.location, | ||
id: row.id + '-separator', | ||
cells: row.cells.map((cell, j) => ({ | ||
location: cell.location, | ||
value: new Array(maxWidths[j] + 1).join('-'), | ||
})), | ||
}; | ||
s += prettyTableRow(separatorRow, level, maxWidths, syntax); | ||
} | ||
n++; | ||
} | ||
return indent + tags.map((tag) => tag.name).join(' ') + '\n'; | ||
return s; | ||
} | ||
function prettyTableRow(row, level, maxWidths, syntax) { | ||
const actualLevel = syntax === 'markdown' ? 1 : level; | ||
return `${spaces(actualLevel)}| ${row.cells | ||
.map((cell, j) => { | ||
const escapedCellValue = escapeCell(cell.value); | ||
const spaceCount = maxWidths[j] - escapedCellValue.length; | ||
const spaces = new Array(spaceCount + 1).join(' '); | ||
return isNumeric(escapedCellValue) ? spaces + escapedCellValue : escapedCellValue + spaces; | ||
}) | ||
.join(' | ')} |\n`; | ||
} | ||
function escapeCell(s) { | ||
let e = ''; | ||
const characters = s.split(''); | ||
for (const c of characters) { | ||
switch (c) { | ||
case '\\': | ||
e += '\\\\'; | ||
break; | ||
case '\n': | ||
e += '\\n'; | ||
break; | ||
case '|': | ||
e += '\\|'; | ||
break; | ||
default: | ||
e += c; | ||
} | ||
} | ||
return e; | ||
} | ||
exports.escapeCell = escapeCell; | ||
function isNumeric(s) { | ||
return !isNaN(parseFloat(s)); | ||
} | ||
//# sourceMappingURL=pretty.js.map |
@@ -39,3 +39,3 @@ "use strict"; | ||
it('returns a deep copy', () => { | ||
const gherkinDocument = parse_1.default(`@featureTag | ||
const gherkinDocument = (0, parse_1.default)(`@featureTag | ||
Feature: hello | ||
@@ -70,3 +70,3 @@ This feature has a description | ||
it('filters one scenario', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -81,3 +81,3 @@ Scenario: Saturn | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -91,3 +91,3 @@ | ||
it('keeps scenario with search hit in step', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -102,3 +102,3 @@ Scenario: Saturn | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -112,3 +112,3 @@ | ||
it('does not leave null object as a feature child', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -126,3 +126,3 @@ Scenario: Saturn | ||
it('keeps a hit scenario even when no steps match', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -137,3 +137,3 @@ Scenario: Saturn | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -148,3 +148,3 @@ | ||
xit('keeps a hit background', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -166,3 +166,3 @@ Background: Space | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -181,3 +181,3 @@ | ||
it('keeps a hit in background step', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -197,3 +197,3 @@ Background: Space | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -218,3 +218,3 @@ | ||
xit('keeps scenario in rule', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -234,3 +234,3 @@ Rule: Galaxy | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -249,3 +249,3 @@ | ||
it('keeps scenario and background in rule', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -265,3 +265,3 @@ Rule: Galaxy | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -283,3 +283,3 @@ | ||
it('only keeps rule and its content', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -296,3 +296,3 @@ Scenario: Milky Way | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -308,3 +308,3 @@ | ||
it('return a feature and keep scenario', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -319,3 +319,3 @@ Scenario: Saturn | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument); | ||
const newSource = pretty_1.default(newGherkinDocument); | ||
const newSource = (0, pretty_1.default)(newGherkinDocument, 'gherkin'); | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -332,3 +332,3 @@ | ||
it('returns null when no hit found', () => { | ||
const gherkinDocument = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -349,3 +349,3 @@ Scenario: Saturn | ||
it('is called for each steps', () => { | ||
const source = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -359,3 +359,3 @@ Scenario: Earth | ||
}); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
assert_1.default.deepEqual(stepText, ['it is a planet']); | ||
@@ -366,3 +366,3 @@ }); | ||
it('is called for each scenarios', () => { | ||
const source = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -379,3 +379,3 @@ Scenario: Earth | ||
}); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
assert_1.default.deepEqual(scenarioName, ['Earth', 'Saturn']); | ||
@@ -386,3 +386,3 @@ }); | ||
it('is called for each backgrounds', () => { | ||
const source = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -397,3 +397,3 @@ Background: Milky Way | ||
}); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
assert_1.default.deepEqual(backgroundName, ['Milky Way']); | ||
@@ -404,3 +404,3 @@ }); | ||
it('is called for each rules', () => { | ||
const source = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -419,3 +419,3 @@ Rule: On a planet | ||
}); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
assert_1.default.deepEqual(ruleName, ['On a planet', 'On an exoplanet']); | ||
@@ -426,3 +426,3 @@ }); | ||
it('is called for each features', () => { | ||
const source = parse_1.default(`Feature: Solar System | ||
const gherkinDocument = (0, parse_1.default)(`Feature: Solar System | ||
@@ -441,3 +441,3 @@ Rule: On a planet | ||
}); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
assert_1.default.deepEqual(featureName, ['Solar System']); | ||
@@ -449,5 +449,5 @@ }); | ||
it('does not fail with empty/commented documents', () => { | ||
const source = parse_1.default('# Feature: Solar System'); | ||
const gherkinDocument = (0, parse_1.default)('# Feature: Solar System'); | ||
const astWalker = new GherkinDocumentWalker_1.default(); | ||
astWalker.walkGherkinDocument(source); | ||
astWalker.walkGherkinDocument(gherkinDocument); | ||
}); | ||
@@ -454,0 +454,0 @@ }); |
@@ -19,8 +19,8 @@ "use strict"; | ||
const fs_1 = __importDefault(require("fs")); | ||
const glob_1 = __importDefault(require("glob")); | ||
const fast_glob_1 = __importDefault(require("fast-glob")); | ||
const util_1 = require("util"); | ||
const asyncPipeline = util_1.promisify(stream_1.pipeline); | ||
const asyncPipeline = (0, util_1.promisify)(stream_1.pipeline); | ||
describe('Walking with messages', () => { | ||
const localMessageFiles = glob_1.default.sync(`${__dirname}/messages/**/*.ndjson`); | ||
const tckMessageFiles = glob_1.default.sync(`${__dirname}/../../../compatibility-kit/javascript/features/**/*.ndjson`); | ||
const localMessageFiles = fast_glob_1.default.sync(`${__dirname}/messages/**/*.ndjson`); | ||
const tckMessageFiles = fast_glob_1.default.sync(`${__dirname}/../../../compatibility-kit/javascript/features/**/*.ndjson`); | ||
const messageFiles = [].concat(localMessageFiles, tckMessageFiles); | ||
@@ -27,0 +27,0 @@ for (const messageFile of messageFiles) { |
import * as messages from '@cucumber/messages'; | ||
export default function parse(source: string): messages.GherkinDocument; | ||
import { GherkinClassicTokenMatcher, GherkinInMarkdownTokenMatcher } from '@cucumber/gherkin'; | ||
export default function parse(source: string, tokenMatcher?: GherkinClassicTokenMatcher | GherkinInMarkdownTokenMatcher): messages.GherkinDocument; | ||
//# sourceMappingURL=parse.d.ts.map |
@@ -24,10 +24,16 @@ "use strict"; | ||
const gherkin_1 = require("@cucumber/gherkin"); | ||
function parse(source) { | ||
function parse(source, tokenMatcher = new gherkin_1.GherkinClassicTokenMatcher()) { | ||
const newId = messages.IdGenerator.uuid(); | ||
const parser = new gherkin_1.Parser(new gherkin_1.AstBuilder(newId), new gherkin_1.GherkinClassicTokenMatcher()); | ||
const gherkinDocument = parser.parse(source); | ||
gherkinDocument.uri = ''; | ||
return gherkinDocument; | ||
const parser = new gherkin_1.Parser(new gherkin_1.AstBuilder(newId), tokenMatcher); | ||
try { | ||
const gherkinDocument = parser.parse(source); | ||
gherkinDocument.uri = ''; | ||
return gherkinDocument; | ||
} | ||
catch (err) { | ||
err.message += `\n${source}`; | ||
throw err; | ||
} | ||
} | ||
exports.default = parse; | ||
//# sourceMappingURL=parse.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -7,10 +26,27 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
const assert_1 = __importDefault(require("assert")); | ||
const path_1 = __importDefault(require("path")); | ||
const parse_1 = __importDefault(require("./parse")); | ||
const pretty_1 = __importDefault(require("../src/pretty")); | ||
describe('PrettyFormatter', () => { | ||
it('renders a feature with no scenarios', () => { | ||
assertPrettyIdentical('Feature: hello\n'); | ||
const pretty_1 = __importStar(require("../src/pretty")); | ||
const gherkin_1 = require("@cucumber/gherkin"); | ||
const fast_glob_1 = __importDefault(require("fast-glob")); | ||
const fs_1 = __importDefault(require("fs")); | ||
describe('pretty', () => { | ||
it('renders an empty file', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(''); | ||
}); | ||
it('renders the language header if it is not "en"', () => { | ||
checkGherkinToAstToGherkin(`# language: no | ||
Egenskap: hallo | ||
`); | ||
}); | ||
it('renders a feature with empty scenarios', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Scenario: Two | ||
`); | ||
}); | ||
it('renders a feature with two scenarios', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -25,3 +61,3 @@ Scenario: one | ||
it('renders a feature with two scenarios in a rule', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -38,3 +74,3 @@ Rule: ok | ||
it('renders a feature with background and scenario', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -49,3 +85,3 @@ Background: bbb | ||
it('renders a rule with background and scenario', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -62,3 +98,3 @@ Rule: machin | ||
it('renders tags when set', () => { | ||
assertPrettyIdentical(`@featureTag | ||
checkGherkinToAstToMarkdownToAstToGherkin(`@featureTag | ||
Feature: hello | ||
@@ -76,4 +112,73 @@ | ||
}); | ||
it('renders examples tables', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a a <text> and a <number> | ||
Examples: some data | ||
| text | number | | ||
| a | 1 | | ||
| ab | 10 | | ||
| abc | 100 | | ||
`); | ||
}); | ||
it('renders data tables', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a data table: | ||
| text | numbers | | ||
| a | 1 | | ||
| ab | 10 | | ||
| abc | 100 | | ||
`); | ||
}); | ||
describe('DocString', () => { | ||
it('is rendered with type', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a doc string: | ||
\`\`\`json | ||
{ | ||
"foo": "bar" | ||
} | ||
\`\`\` | ||
`); | ||
}); | ||
it('escapes DocString separators', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a doc string: | ||
\`\`\` | ||
2 | ||
\`\` | ||
3 | ||
\\\`\\\`\\\` | ||
4 | ||
\\\`\\\`\\\`\` | ||
5 | ||
\\\`\\\`\\\`\`\` | ||
\`\`\` | ||
`); | ||
}); | ||
}); | ||
xit('renders comments', () => { | ||
checkGherkinToAstToGherkin(`# one | ||
Feature: hello | ||
Scenario: one | ||
# two | ||
Given a doc string: | ||
""" | ||
a | ||
\\"\\"\\" | ||
b | ||
""" | ||
`); | ||
}); | ||
it('renders descriptions when set', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToGherkin(`Feature: hello | ||
So this is a feature | ||
@@ -95,7 +200,66 @@ | ||
}); | ||
const featureFiles = fast_glob_1.default.sync(`${__dirname}/../../../gherkin/testdata/good/*.feature`); | ||
for (const featureFile of featureFiles) { | ||
const relativePath = path_1.default.relative(__dirname, featureFile); | ||
it(`renders ${relativePath}`, () => { | ||
var _a; | ||
const gherkinSource = fs_1.default.readFileSync(featureFile, 'utf-8'); | ||
const gherkinDocument = (0, parse_1.default)(gherkinSource, new gherkin_1.GherkinClassicTokenMatcher()); | ||
const formattedGherkinSource = (0, pretty_1.default)(gherkinDocument, 'gherkin'); | ||
const language = ((_a = gherkinDocument.feature) === null || _a === void 0 ? void 0 : _a.language) || 'en'; | ||
const newGherkinDocument = checkGherkinToAstToGherkin(formattedGherkinSource, language); | ||
(0, assert_1.default)(newGherkinDocument); | ||
// TODO: comments | ||
if (gherkinDocument.comments.length === 0) { | ||
assert_1.default.deepStrictEqual(neutralize(newGherkinDocument), neutralize(gherkinDocument)); | ||
} | ||
}); | ||
} | ||
describe('escapeCell', () => { | ||
it('escapes nothing', () => { | ||
assert_1.default.strictEqual((0, pretty_1.escapeCell)('hello'), 'hello'); | ||
}); | ||
it('escapes newline', () => { | ||
assert_1.default.strictEqual((0, pretty_1.escapeCell)('\n'), '\\n'); | ||
}); | ||
it('escapes pipe', () => { | ||
assert_1.default.strictEqual((0, pretty_1.escapeCell)('|'), '\\|'); | ||
}); | ||
it('escapes backslash', () => { | ||
assert_1.default.strictEqual((0, pretty_1.escapeCell)('\\'), '\\\\'); | ||
}); | ||
}); | ||
}); | ||
function assertPrettyIdentical(source) { | ||
const gherkinDocument = parse_1.default(source); | ||
assert_1.default.strictEqual(pretty_1.default(gherkinDocument), source); | ||
function checkGherkinToAstToMarkdownToAstToGherkin(gherkinSource) { | ||
const gherkinDocument = (0, parse_1.default)(gherkinSource, new gherkin_1.GherkinClassicTokenMatcher()); | ||
// console.log({gherkinDocument}) | ||
const markdownSource = (0, pretty_1.default)(gherkinDocument, 'markdown'); | ||
// console.log(`<Markdown>${markdownSource}</Markdown>`) | ||
const markdownGherkinDocument = (0, parse_1.default)(markdownSource, new gherkin_1.GherkinInMarkdownTokenMatcher()); | ||
// console.log({markdownGherkinDocument}) | ||
const newGherkinSource = (0, pretty_1.default)(markdownGherkinDocument, 'gherkin'); | ||
// console.log(`<Gherkin>${newGherkinSource}</Gherkin>`) | ||
assert_1.default.strictEqual(newGherkinSource, gherkinSource); | ||
} | ||
function checkGherkinToAstToGherkin(gherkinSource, language = 'en') { | ||
const gherkinDocument = (0, parse_1.default)(gherkinSource, new gherkin_1.GherkinClassicTokenMatcher(language)); | ||
const newGherkinSource = (0, pretty_1.default)(gherkinDocument, 'gherkin'); | ||
// console.log(`<Gherkin>${newGherkinSource}</Gherkin>`) | ||
assert_1.default.strictEqual(newGherkinSource, gherkinSource); | ||
return gherkinDocument; | ||
} | ||
function neutralize(gherkinDocument) { | ||
const json = JSON.stringify(gherkinDocument, (key, value) => { | ||
if ('id' === key) { | ||
return 'id'; | ||
} | ||
else if (['column', 'line'].includes(key)) { | ||
return '0'; | ||
} | ||
else { | ||
return value; | ||
} | ||
}, 2); | ||
return JSON.parse(json); | ||
} | ||
//# sourceMappingURL=prettyTest.js.map |
@@ -40,3 +40,3 @@ "use strict"; | ||
const util_1 = require("util"); | ||
const pipelinePromise = util_1.promisify(stream_1.pipeline); | ||
const pipelinePromise = (0, util_1.promisify)(stream_1.pipeline); | ||
describe('Query', () => { | ||
@@ -43,0 +43,0 @@ let gherkinQuery; |
{ | ||
"name": "@cucumber/gherkin-utils", | ||
"version": "6.0.0", | ||
"version": "7.0.0", | ||
"description": "Various Gherkin utilities", | ||
"main": "dist/src/index.js", | ||
"types": "dist/src/index.d.ts", | ||
"bin": { | ||
"@cucumber/gherkin-utils": "bin/gherkin-utils" | ||
}, | ||
"scripts": { | ||
@@ -25,16 +28,16 @@ "test": "mocha", | ||
"devDependencies": { | ||
"@cucumber/gherkin": "^20.0.0", | ||
"@cucumber/gherkin-streams": "^3.0.0", | ||
"@cucumber/gherkin": "^21.0.0", | ||
"@cucumber/gherkin-streams": "^4.0.0", | ||
"@cucumber/message-streams": "^3.0.0", | ||
"@types/glob": "7.1.4", | ||
"@types/mocha": "8.2.3", | ||
"@types/node": "14.17.5", | ||
"glob": "7.1.7", | ||
"mocha": "9.0.2", | ||
"ts-node": "10.0.0", | ||
"typescript": "4.3.5" | ||
"@types/mocha": "9.0.0", | ||
"@types/node": "14.17.14", | ||
"fast-glob": "3.2.7", | ||
"mocha": "9.1.1", | ||
"ts-node": "10.2.1", | ||
"typescript": "4.4.2" | ||
}, | ||
"dependencies": { | ||
"@cucumber/messages": "^17.0.0", | ||
"@teppeis/multimaps": "2.0.0" | ||
"@cucumber/messages": "^17.1.0", | ||
"@teppeis/multimaps": "2.0.0", | ||
"commander": "8.1.0" | ||
}, | ||
@@ -41,0 +44,0 @@ "directories": { |
@@ -0,1 +1,2 @@ | ||
// This file is DEPRECATED - use ./walkGherkinDocument instead | ||
import * as messages from '@cucumber/messages' | ||
@@ -2,0 +3,0 @@ |
@@ -0,1 +1,3 @@ | ||
export * from './walkGherkinDocument' | ||
export * from './GherkinDocumentHandlers' | ||
import pretty from './pretty' | ||
@@ -2,0 +4,0 @@ import Query from './Query' |
import * as messages from '@cucumber/messages' | ||
import { walkGherkinDocument } from './walkGherkinDocument' | ||
export default function pretty(gherkinDocument: messages.GherkinDocument): string { | ||
const feature = gherkinDocument.feature | ||
let s = prettyTags(feature.tags) | ||
export type Syntax = 'markdown' | 'gherkin' | ||
s += feature.keyword + ': ' + feature.name + '\n' | ||
if (feature.description) { | ||
s += feature.description + '\n' | ||
} | ||
for (const child of feature.children) { | ||
if (child.background) { | ||
s += prettyStepContainer(child.background, ' ') | ||
} else if (child.scenario) { | ||
s += prettyStepContainer(child.scenario, ' ') | ||
} else if (child.rule) { | ||
s += `\n ${child.rule.keyword}: ${child.rule.name}\n` | ||
if (child.rule.description) { | ||
s += child.rule.description + '\n' | ||
} | ||
for (const ruleChild of child.rule.children) { | ||
if (ruleChild.background) { | ||
s += prettyStepContainer(ruleChild.background, ' ') | ||
export default function pretty( | ||
gherkinDocument: messages.GherkinDocument, | ||
syntax: Syntax = 'gherkin' | ||
): string { | ||
let scenarioLevel = 1 | ||
return walkGherkinDocument<string>(gherkinDocument, '', { | ||
feature(feature, content) { | ||
return content | ||
.concat(prettyLanguageHeader(feature.language)) | ||
.concat(prettyKeywordContainer(feature, syntax, 0)) | ||
}, | ||
rule(rule, content) { | ||
scenarioLevel = 2 | ||
return content.concat(prettyKeywordContainer(rule, syntax, 1)) | ||
}, | ||
background(background, content) { | ||
return content.concat(prettyKeywordContainer(background, syntax, scenarioLevel)) | ||
}, | ||
scenario(scenario, content) { | ||
return content.concat(prettyKeywordContainer(scenario, syntax, scenarioLevel)) | ||
}, | ||
examples(examples, content) { | ||
const tableRows = examples.tableHeader ? [examples.tableHeader, ...examples.tableBody] : [] | ||
return content | ||
.concat(prettyKeywordContainer(examples, syntax, scenarioLevel + 1)) | ||
.concat(prettyTableRows(tableRows, syntax, scenarioLevel + 2)) | ||
}, | ||
step(step, content) { | ||
return content | ||
.concat(stepPrefix(scenarioLevel + 1, syntax)) | ||
.concat(step.keyword) | ||
.concat(step.text) | ||
.concat('\n') | ||
}, | ||
dataTable(dataTable, content) { | ||
const level = syntax === 'markdown' ? 1 : scenarioLevel + 2 | ||
return content.concat(prettyTableRows(dataTable.rows || [], syntax, level)) | ||
}, | ||
docString(docString, content) { | ||
const delimiter = makeDocStringDelimiter(syntax, docString) | ||
const level = syntax === 'markdown' ? 1 : scenarioLevel + 2 | ||
const indent = spaces(level) | ||
let docStringContent = docString.content.replace(/^/gm, indent) | ||
if (syntax === 'gherkin') { | ||
if (docString.delimiter === '"""') { | ||
docStringContent = docStringContent.replace(/"""/gm, '\\"\\"\\"') | ||
} else { | ||
docStringContent = docStringContent.replace(/```/gm, '\\`\\`\\`') | ||
} | ||
if (ruleChild.scenario) { | ||
s += prettyStepContainer(ruleChild.scenario, ' ') | ||
} | ||
} | ||
} | ||
} | ||
return s | ||
return content | ||
.concat(indent) | ||
.concat(delimiter) | ||
.concat(docString.mediaType || '') | ||
.concat('\n') | ||
.concat(docStringContent) | ||
.concat('\n') | ||
.concat(indent) | ||
.concat(delimiter) | ||
.concat('\n') | ||
}, | ||
}) | ||
} | ||
function prettyStepContainer( | ||
stepContainer: messages.Scenario | messages.Background, | ||
indent: string | ||
function prettyLanguageHeader(language: string | undefined): string { | ||
return language === 'en' ? '' : `# language: ${language}\n` | ||
} | ||
function prettyKeywordContainer( | ||
stepContainer: | ||
| messages.Feature | ||
| messages.Scenario | ||
| messages.Rule | ||
| messages.Examples | ||
| messages.Background, | ||
syntax: Syntax, | ||
level: number | ||
): string { | ||
const scenario: messages.Scenario = 'tags' in stepContainer ? stepContainer : null | ||
const tags: readonly messages.Tag[] = scenario?.tags || [] | ||
let s = `\n${prettyTags(tags, indent)}${indent}${stepContainer.keyword}: ${stepContainer.name}\n` | ||
if (stepContainer.description) { | ||
s += stepContainer.description + '\n\n' | ||
const tags: readonly messages.Tag[] = 'tags' in stepContainer ? stepContainer.tags : [] | ||
const stepCount = 'steps' in stepContainer ? stepContainer.steps.length : 0 | ||
const description = prettyDescription(stepContainer.description, syntax) | ||
return '' | ||
.concat(level === 0 ? '' : '\n') | ||
.concat(prettyTags(tags, syntax, level)) | ||
.concat(keywordPrefix(level, syntax)) | ||
.concat(stepContainer.keyword) | ||
.concat(': ') | ||
.concat(stepContainer.name) | ||
.concat('\n') | ||
.concat(description) | ||
.concat(description && stepCount > 0 ? '\n' : '') | ||
} | ||
function prettyDescription(description: string, syntax: Syntax): string { | ||
if (!description) return '' | ||
if (syntax === 'gherkin') return description + '\n' | ||
else return description.replace(/^\s*/gm, '') + '\n' | ||
} | ||
function prettyTags(tags: readonly messages.Tag[], syntax: Syntax, level: number): string { | ||
if (tags === undefined || tags.length == 0) { | ||
return '' | ||
} | ||
const prefix = syntax === 'gherkin' ? spaces(level) : '' | ||
const tagQuote = syntax === 'gherkin' ? '' : '`' | ||
return prefix + tags.map((tag) => `${tagQuote}${tag.name}${tagQuote}`).join(' ') + '\n' | ||
} | ||
for (const step of stepContainer.steps) { | ||
s += `${indent} ${step.keyword}${step.text}\n` | ||
function keywordPrefix(level: number, syntax: Syntax): string { | ||
if (syntax === 'markdown') { | ||
return new Array(level + 2).join('#') + ' ' | ||
} else { | ||
return spaces(level) | ||
} | ||
} | ||
if (scenario) { | ||
for (const example of scenario.examples) { | ||
s += prettyExample(example, `${indent} `) | ||
} | ||
function stepPrefix(level: number, syntax: Syntax): string { | ||
if (syntax === 'markdown') { | ||
return '* ' | ||
} else { | ||
return new Array(level + 1).join(' ') | ||
} | ||
return s | ||
} | ||
function prettyExample(example: messages.Examples, indent: string): string { | ||
let s = `\n${indent}Examples: ${example.name}\n` | ||
function spaces(level: number): string { | ||
return new Array(level + 1).join(' ') | ||
} | ||
s += prettyTableRow(example.tableHeader, `${indent} `) | ||
for (const row of example.tableBody) { | ||
s += prettyTableRow(row, `${indent} `) | ||
function makeDocStringDelimiter(syntax: Syntax, docString: messages.DocString) { | ||
if (syntax === 'gherkin') { | ||
return docString.delimiter.substring(0, 3) | ||
} | ||
// The length of the fenced code block delimiter is three backticks when the content inside doesn't have backticks. | ||
// If the content inside has three or more backticks, the number of backticks in the delimiter must be at least one more | ||
// https://github.github.com/gfm/#fenced-code-blocks | ||
const threeOrMoreBackticks = /(```+)/g | ||
let maxContentBackTickCount = 2 | ||
let match | ||
do { | ||
match = threeOrMoreBackticks.exec(docString.content) | ||
if (match) { | ||
maxContentBackTickCount = Math.max(maxContentBackTickCount, match[1].length) | ||
} | ||
} while (match) | ||
// Return a delimiter with one more backtick than the max number of backticks in the contents (3 ny default) | ||
return new Array(maxContentBackTickCount + 2).join('`') | ||
} | ||
function prettyTableRows( | ||
tableRows: readonly messages.TableRow[], | ||
syntax: Syntax, | ||
level: number | ||
): string { | ||
if (tableRows.length === 0) return '' | ||
const maxWidths: number[] = new Array(tableRows[0].cells.length).fill(0) | ||
tableRows.forEach((tableRow) => { | ||
tableRow.cells.forEach((tableCell, j) => { | ||
maxWidths[j] = Math.max(maxWidths[j], escapeCell(tableCell.value).length) | ||
}) | ||
}) | ||
let n = 0 | ||
let s = '' | ||
for (const row of tableRows) { | ||
s += prettyTableRow(row, level, maxWidths, syntax) | ||
if (n === 0 && syntax === 'markdown') { | ||
const separatorRow: messages.TableRow = { | ||
location: row.location, | ||
id: row.id + '-separator', | ||
cells: row.cells.map((cell, j) => ({ | ||
location: cell.location, | ||
value: new Array(maxWidths[j] + 1).join('-'), | ||
})), | ||
} | ||
s += prettyTableRow(separatorRow, level, maxWidths, syntax) | ||
} | ||
n++ | ||
} | ||
return s | ||
} | ||
function prettyTableRow(row: messages.TableRow, indent: string): string { | ||
return `${indent}| ${row.cells.map((cell) => cell.value).join(' | ')} |\n` | ||
function prettyTableRow( | ||
row: messages.TableRow, | ||
level: number, | ||
maxWidths: readonly number[], | ||
syntax: Syntax | ||
): string { | ||
const actualLevel = syntax === 'markdown' ? 1 : level | ||
return `${spaces(actualLevel)}| ${row.cells | ||
.map((cell, j) => { | ||
const escapedCellValue = escapeCell(cell.value) | ||
const spaceCount = maxWidths[j] - escapedCellValue.length | ||
const spaces = new Array(spaceCount + 1).join(' ') | ||
return isNumeric(escapedCellValue) ? spaces + escapedCellValue : escapedCellValue + spaces | ||
}) | ||
.join(' | ')} |\n` | ||
} | ||
function prettyTags(tags: readonly messages.Tag[], indent = ''): string { | ||
if (tags === undefined || tags.length == 0) { | ||
return '' | ||
export function escapeCell(s: string) { | ||
let e = '' | ||
const characters = s.split('') | ||
for (const c of characters) { | ||
switch (c) { | ||
case '\\': | ||
e += '\\\\' | ||
break | ||
case '\n': | ||
e += '\\n' | ||
break | ||
case '|': | ||
e += '\\|' | ||
break | ||
default: | ||
e += c | ||
} | ||
} | ||
return e | ||
} | ||
return indent + tags.map((tag) => tag.name).join(' ') + '\n' | ||
function isNumeric(s: string) { | ||
return !isNaN(parseFloat(s)) | ||
} |
@@ -80,3 +80,3 @@ import assert from 'assert' | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -105,3 +105,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -151,3 +151,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -184,3 +184,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -219,3 +219,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -260,3 +260,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -295,3 +295,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -330,3 +330,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -357,3 +357,3 @@ | ||
const newGherkinDocument = walker.walkGherkinDocument(gherkinDocument) | ||
const newSource = pretty(newGherkinDocument) | ||
const newSource = pretty(newGherkinDocument, 'gherkin') | ||
const expectedNewSource = `Feature: Solar System | ||
@@ -389,3 +389,3 @@ | ||
it('is called for each steps', () => { | ||
const source = parse(`Feature: Solar System | ||
const gherkinDocument = parse(`Feature: Solar System | ||
@@ -403,3 +403,3 @@ Scenario: Earth | ||
) | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
@@ -412,3 +412,3 @@ assert.deepEqual(stepText, ['it is a planet']) | ||
it('is called for each scenarios', () => { | ||
const source = parse(`Feature: Solar System | ||
const gherkinDocument = parse(`Feature: Solar System | ||
@@ -429,3 +429,3 @@ Scenario: Earth | ||
) | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
@@ -438,3 +438,3 @@ assert.deepEqual(scenarioName, ['Earth', 'Saturn']) | ||
it('is called for each backgrounds', () => { | ||
const source = parse(`Feature: Solar System | ||
const gherkinDocument = parse(`Feature: Solar System | ||
@@ -453,3 +453,3 @@ Background: Milky Way | ||
) | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
@@ -462,3 +462,3 @@ assert.deepEqual(backgroundName, ['Milky Way']) | ||
it('is called for each rules', () => { | ||
const source = parse(`Feature: Solar System | ||
const gherkinDocument = parse(`Feature: Solar System | ||
@@ -481,3 +481,3 @@ Rule: On a planet | ||
) | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
@@ -490,3 +490,3 @@ assert.deepEqual(ruleName, ['On a planet', 'On an exoplanet']) | ||
it('is called for each features', () => { | ||
const source = parse(`Feature: Solar System | ||
const gherkinDocument = parse(`Feature: Solar System | ||
@@ -509,3 +509,3 @@ Rule: On a planet | ||
) | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
@@ -519,8 +519,8 @@ assert.deepEqual(featureName, ['Solar System']) | ||
it('does not fail with empty/commented documents', () => { | ||
const source = parse('# Feature: Solar System') | ||
const gherkinDocument = parse('# Feature: Solar System') | ||
const astWalker = new GherkinDocumentWalker() | ||
astWalker.walkGherkinDocument(source) | ||
astWalker.walkGherkinDocument(gherkinDocument) | ||
}) | ||
}) | ||
}) |
@@ -7,3 +7,3 @@ import * as messages from '@cucumber/messages' | ||
import fs from 'fs' | ||
import glob from 'glob' | ||
import fg from 'fast-glob' | ||
import { promisify } from 'util' | ||
@@ -14,4 +14,4 @@ | ||
describe('Walking with messages', () => { | ||
const localMessageFiles = glob.sync(`${__dirname}/messages/**/*.ndjson`) | ||
const tckMessageFiles = glob.sync( | ||
const localMessageFiles = fg.sync(`${__dirname}/messages/**/*.ndjson`) | ||
const tckMessageFiles = fg.sync( | ||
`${__dirname}/../../../compatibility-kit/javascript/features/**/*.ndjson` | ||
@@ -18,0 +18,0 @@ ) |
import * as messages from '@cucumber/messages' | ||
import { AstBuilder, Parser, GherkinClassicTokenMatcher } from '@cucumber/gherkin' | ||
import { | ||
AstBuilder, | ||
Parser, | ||
GherkinClassicTokenMatcher, | ||
GherkinInMarkdownTokenMatcher, | ||
} from '@cucumber/gherkin' | ||
export default function parse(source: string): messages.GherkinDocument { | ||
export default function parse( | ||
source: string, | ||
tokenMatcher: | ||
| GherkinClassicTokenMatcher | ||
| GherkinInMarkdownTokenMatcher = new GherkinClassicTokenMatcher() | ||
): messages.GherkinDocument { | ||
const newId = messages.IdGenerator.uuid() | ||
const parser = new Parser(new AstBuilder(newId), new GherkinClassicTokenMatcher()) | ||
const gherkinDocument = parser.parse(source) | ||
gherkinDocument.uri = '' | ||
return gherkinDocument | ||
const parser = new Parser(new AstBuilder(newId), tokenMatcher) | ||
try { | ||
const gherkinDocument = parser.parse(source) | ||
gherkinDocument.uri = '' | ||
return gherkinDocument | ||
} catch (err) { | ||
err.message += `\n${source}` | ||
throw err | ||
} | ||
} |
import assert from 'assert' | ||
import path from 'path' | ||
import parse from './parse' | ||
import pretty from '../src/pretty' | ||
import pretty, { escapeCell } from '../src/pretty' | ||
import { GherkinClassicTokenMatcher, GherkinInMarkdownTokenMatcher } from '@cucumber/gherkin' | ||
import fg from 'fast-glob' | ||
import fs from 'fs' | ||
import * as messages from '@cucumber/messages' | ||
describe('PrettyFormatter', () => { | ||
it('renders a feature with no scenarios', () => { | ||
assertPrettyIdentical('Feature: hello\n') | ||
describe('pretty', () => { | ||
it('renders an empty file', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin('') | ||
}) | ||
it('renders the language header if it is not "en"', () => { | ||
checkGherkinToAstToGherkin(`# language: no | ||
Egenskap: hallo | ||
`) | ||
}) | ||
it('renders a feature with empty scenarios', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Scenario: Two | ||
`) | ||
}) | ||
it('renders a feature with two scenarios', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -22,3 +42,3 @@ Scenario: one | ||
it('renders a feature with two scenarios in a rule', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -36,3 +56,3 @@ Rule: ok | ||
it('renders a feature with background and scenario', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -48,3 +68,3 @@ Background: bbb | ||
it('renders a rule with background and scenario', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
@@ -62,3 +82,3 @@ Rule: machin | ||
it('renders tags when set', () => { | ||
assertPrettyIdentical(`@featureTag | ||
checkGherkinToAstToMarkdownToAstToGherkin(`@featureTag | ||
Feature: hello | ||
@@ -77,4 +97,78 @@ | ||
it('renders examples tables', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a a <text> and a <number> | ||
Examples: some data | ||
| text | number | | ||
| a | 1 | | ||
| ab | 10 | | ||
| abc | 100 | | ||
`) | ||
}) | ||
it('renders data tables', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a data table: | ||
| text | numbers | | ||
| a | 1 | | ||
| ab | 10 | | ||
| abc | 100 | | ||
`) | ||
}) | ||
describe('DocString', () => { | ||
it('is rendered with type', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a doc string: | ||
\`\`\`json | ||
{ | ||
"foo": "bar" | ||
} | ||
\`\`\` | ||
`) | ||
}) | ||
it('escapes DocString separators', () => { | ||
checkGherkinToAstToMarkdownToAstToGherkin(`Feature: hello | ||
Scenario: one | ||
Given a doc string: | ||
\`\`\` | ||
2 | ||
\`\` | ||
3 | ||
\\\`\\\`\\\` | ||
4 | ||
\\\`\\\`\\\`\` | ||
5 | ||
\\\`\\\`\\\`\`\` | ||
\`\`\` | ||
`) | ||
}) | ||
}) | ||
xit('renders comments', () => { | ||
checkGherkinToAstToGherkin(`# one | ||
Feature: hello | ||
Scenario: one | ||
# two | ||
Given a doc string: | ||
""" | ||
a | ||
\\"\\"\\" | ||
b | ||
""" | ||
`) | ||
}) | ||
it('renders descriptions when set', () => { | ||
assertPrettyIdentical(`Feature: hello | ||
checkGherkinToAstToGherkin(`Feature: hello | ||
So this is a feature | ||
@@ -96,7 +190,78 @@ | ||
}) | ||
const featureFiles = fg.sync(`${__dirname}/../../../gherkin/testdata/good/*.feature`) | ||
for (const featureFile of featureFiles) { | ||
const relativePath = path.relative(__dirname, featureFile) | ||
it(`renders ${relativePath}`, () => { | ||
const gherkinSource = fs.readFileSync(featureFile, 'utf-8') | ||
const gherkinDocument = parse(gherkinSource, new GherkinClassicTokenMatcher()) | ||
const formattedGherkinSource = pretty(gherkinDocument, 'gherkin') | ||
const language = gherkinDocument.feature?.language || 'en' | ||
const newGherkinDocument = checkGherkinToAstToGherkin(formattedGherkinSource, language) | ||
assert(newGherkinDocument) | ||
// TODO: comments | ||
if (gherkinDocument.comments.length === 0) { | ||
assert.deepStrictEqual(neutralize(newGherkinDocument), neutralize(gherkinDocument)) | ||
} | ||
}) | ||
} | ||
describe('escapeCell', () => { | ||
it('escapes nothing', () => { | ||
assert.strictEqual(escapeCell('hello'), 'hello') | ||
}) | ||
it('escapes newline', () => { | ||
assert.strictEqual(escapeCell('\n'), '\\n') | ||
}) | ||
it('escapes pipe', () => { | ||
assert.strictEqual(escapeCell('|'), '\\|') | ||
}) | ||
it('escapes backslash', () => { | ||
assert.strictEqual(escapeCell('\\'), '\\\\') | ||
}) | ||
}) | ||
}) | ||
function assertPrettyIdentical(source: string) { | ||
const gherkinDocument = parse(source) | ||
assert.strictEqual(pretty(gherkinDocument), source) | ||
function checkGherkinToAstToMarkdownToAstToGherkin(gherkinSource: string) { | ||
const gherkinDocument = parse(gherkinSource, new GherkinClassicTokenMatcher()) | ||
// console.log({gherkinDocument}) | ||
const markdownSource = pretty(gherkinDocument, 'markdown') | ||
// console.log(`<Markdown>${markdownSource}</Markdown>`) | ||
const markdownGherkinDocument = parse(markdownSource, new GherkinInMarkdownTokenMatcher()) | ||
// console.log({markdownGherkinDocument}) | ||
const newGherkinSource = pretty(markdownGherkinDocument, 'gherkin') | ||
// console.log(`<Gherkin>${newGherkinSource}</Gherkin>`) | ||
assert.strictEqual(newGherkinSource, gherkinSource) | ||
} | ||
function checkGherkinToAstToGherkin( | ||
gherkinSource: string, | ||
language = 'en' | ||
): messages.GherkinDocument { | ||
const gherkinDocument = parse(gherkinSource, new GherkinClassicTokenMatcher(language)) | ||
const newGherkinSource = pretty(gherkinDocument, 'gherkin') | ||
// console.log(`<Gherkin>${newGherkinSource}</Gherkin>`) | ||
assert.strictEqual(newGherkinSource, gherkinSource) | ||
return gherkinDocument | ||
} | ||
function neutralize(gherkinDocument: messages.GherkinDocument): messages.GherkinDocument { | ||
const json = JSON.stringify( | ||
gherkinDocument, | ||
(key, value) => { | ||
if ('id' === key) { | ||
return 'id' | ||
} else if (['column', 'line'].includes(key)) { | ||
return '0' | ||
} else { | ||
return value | ||
} | ||
}, | ||
2 | ||
) | ||
return JSON.parse(json) | ||
} |
@@ -9,4 +9,5 @@ { | ||
"src", | ||
"test" | ||
"test", | ||
"package.json" | ||
] | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
268108
9
90
4127
3
4
+ Addedcommander@8.1.0
+ Addedcommander@8.1.0(transitive)
Updated@cucumber/messages@^17.1.0