Skip to content

Commit

Permalink
fix: DocStrings and DataTables replacements in Scenario Outlines
Browse files Browse the repository at this point in the history
Refs: Fixes #23
  • Loading branch information
dnotes committed Dec 17, 2024
1 parent 956d422 commit d4d5717
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-news-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"quickpickle": patch
---

Fix DataTables and DocStrings replacements in Scenario Outlines
26 changes: 26 additions & 0 deletions packages/main/gherkin-example/example.feature
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,29 @@ Feature: QuickPickle's Comprehensive Gherkin Syntax Example
Given a "string with \"quotes\""
And `someone` is ${sneaky} \${with} \\${backslashes} \\\${and} \$\{other} \`things\\` 'like' \'quotes\\'

Scenario Outline: DataTables row: <Row>
Given the following datatable:
| Product | Quantity |
| <Product1> | <Qty1> |
| <Product2> | <Qty2> |
Then datatable should contain "<Product1>"

Examples:
| Row | Product1 | Qty1 | Product2 | Qty2 |
| 0 | Widget A | 2 | Widget B | 3 |
| 1 | Widget C | 1 | Widget D | 4 |

Scenario Outline: DocStrings row: <Row>
And the following json:
"""
{
"<Product1>": "<Qty1>",
"<Product2>": "<Qty2>"
}
"""
Then json should contain "<Product1>"

Examples:
| Row | Product1 | Qty1 | Product2 | Qty2 |
| 0 | Widget A | 2 | Widget B | 3 |
| 1 | Widget C | 1 | Widget D | 4 |
52 changes: 43 additions & 9 deletions packages/main/gherkin-example/example.feature.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions packages/main/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { tagsMatch, normalizeTags } from './tags'

import * as Gherkin from '@cucumber/gherkin';
import * as Messages from '@cucumber/messages';
import { fromPairs, pick, escapeRegExp } from "lodash-es";
import { fromPairs, pick, escapeRegExp, replace } from "lodash-es";

const uuidFn = Messages.IdGenerator.uuid();
const builder = new Gherkin.AstBuilder(uuidFn);
Expand Down Expand Up @@ -158,7 +158,7 @@ function renderScenario(child:FeatureChild, config:QuickPickleConfig, tags:strin
return fromPairs(r.cells.map((c,i) => [ '_'+i, c.value ]))
})

function replaceParamNames(t:string, withBraces?:boolean) {
const replaceParamNames = (t:string, withBraces?:boolean) => {
origParamNames.forEach((p,i) => {
t = t.replace(new RegExp(`<${escapeRegExp(p)}>`, 'g'), (withBraces ? `$\{_${i}\}` : `$_${i}`))
})
Expand All @@ -173,7 +173,7 @@ function renderScenario(child:FeatureChild, config:QuickPickleConfig, tags:strin
return text
})

let renderedSteps = renderSteps(child.scenario!.steps.map(s => ({...s, text: replaceParamNames(s.text, true)})), config, sp + ' ', isExploded ? `${explodedIdx+1}` : '')
let renderedSteps = renderSteps(child.scenario!.steps.map(s => ({...s, text: replaceParamNames(s.text, true)})), config, sp + ' ', isExploded ? `${explodedIdx+1}` : '', false, replaceParamNames)

return `
${sp}test${attrs}.for([
Expand Down Expand Up @@ -201,18 +201,16 @@ ${sp}});
}).join('\n\n')
}

function renderSteps(steps:Step[], config:QuickPickleConfig, sp = ' ', explodedText = '', isBackground:boolean = false) {
function renderSteps(steps:Step[], config:QuickPickleConfig, sp = ' ', explodedText = '', isBackground:boolean = false, replaceParamNames:(t:string, withBraces?:boolean) => string = (t) => t) {
let minus = isBackground ? '-' : ''
return steps.map((step,idx) => {

if (step.dataTable) {
let data = JSON.stringify(step.dataTable.rows.map(r => {
return r.cells.map(c => c.value)
}))
let data = `[${step.dataTable.rows.map(r => `[${r.cells.map(c => tl(replaceParamNames(c.value, true))).join(',')}]`).join(',')}]`
return `${sp}await gherkinStep('${getStepType(steps, idx)}', ${tl(step.text)}, state, ${step.location.line}, ${minus}${idx+1}, ${explodedText || 'undefined'}, ${data});`
}
else if (step.docString) {
let data = JSON.stringify(pick(step.docString, ['content','mediaType']))
let data = `{content:${tl(replaceParamNames(step.docString.content, true))}, mediaType:${step.docString?.mediaType ? tl(step.docString.mediaType) : 'null'} }`
return `${sp}await gherkinStep('${getStepType(steps, idx)}', ${tl(step.text)}, state, ${step.location.line}, ${minus}${idx+1}, ${explodedText || 'undefined'}, ${data});`
}

Expand Down
24 changes: 20 additions & 4 deletions packages/main/tests/test.feature
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,26 @@ Feature: Basic Test
| 5 | 8 | 13 |
| 8 | 13 | 21 |
| 13 | 21 | 34 |

Scenario Outline: DataTables row: <Row>

Scenario Outline: Replacements in strings work for <prop>:<value> (<length>)
Given I set the data property "<prop>" to "<value>"
Then the variable "data.<prop>" should be "<value>"
And the variable "data.<prop>" should be <length> characters long

Examples:
| prop | value | length |
| foo | bar | 3 |
| fwah | fwee | 4 |

Scenario Outline: DataTables row: <Row>
Given the following datatable:
| Product | Quantity |
| <Product1> | <Qty1> |
| <Product2> | <Qty2> |
Then datatable should contain "<Product1>"
Then the datatable should contain "<Product1>"
And the datatable should contain "<Qty1>"
And the datatable should contain "<Product2>"
And the datatable should contain "<Qty2>"

Examples:
| Row | Product1 | Qty1 | Product2 | Qty2 |
Expand All @@ -123,7 +136,10 @@ Feature: Basic Test
"<Product2>": "<Qty2>"
}
"""
Then json should contain "<Product1>"
Then the json should contain "<Product1>"
And the json should contain "<Qty1>"
And the json should contain "<Product2>"
And the json should contain "<Qty2>"

Examples:
| Row | Product1 | Qty1 | Product2 | Qty2 |
Expand Down
17 changes: 6 additions & 11 deletions packages/main/tests/tests.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,14 @@ Then('the sum should be {int}', (world, int) => {
expect(world.numbers.reduce((a,b) => a + b, 0)).toBe(int)
})

Then("datatable should contain {string}", (world, value) => {
const values = world.datatable
.rows()
.reduce((prev, curr) => [...curr, ...prev], []);
expect(values.includes(value)).toBe(true);
Then("the datatable should contain {string}", (world, value) => {
const values = JSON.stringify(world.datatable);
expect(values).toMatch(value);
});

Then("json should contain {string}", (world, value) => {
const values = [
...Object.keys(world.json),
...(Object.values(world.json) as string[]).map((v) => v.toString()),
];
expect(values.includes(value)).toBe(true);
Then("the json should contain {string}", (world, value) => {
const values = JSON.stringify(world.json);
expect(values).toMatch(value);
});

When('I set the data variable/value/property {string} to {string}', (world, prop, value) => {
Expand Down

0 comments on commit d4d5717

Please sign in to comment.