Skip to content

Commit 80bb69e

Browse files
authored
Merge pull request #113 from olexale/fix-datatable-content
Fix datatable content
2 parents a1fa97c + fdcd94e commit 80bb69e

4 files changed

Lines changed: 138 additions & 43 deletions

File tree

lib/src/data_table_parser.dart

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ bool hasBddDataTable(List<BddLine> lines) {
88
lines[index].type == LineType.step ||
99
lines[index].type == LineType.dataTableStep;
1010
final isNextLineTable = isTable(lines: lines, index: index + 1);
11-
final isExamplesFormatted = hasExamplesFormat(bddLine: lines[index]);
12-
if (isStep && isNextLineTable && !isExamplesFormatted) {
11+
if (isStep &&
12+
isNextLineTable &&
13+
!isDataTableExamples(lines: lines, index: index)) {
1314
return true;
1415
}
1516
}
@@ -24,9 +25,9 @@ Iterable<BddLine> replaceDataTables(List<BddLine> lines) sync* {
2425
final isNextLineTable = isTable(lines: lines, index: index + 1);
2526
if (isStep && isNextLineTable) {
2627
final table =
27-
!hasExamplesFormat(bddLine: lines[index])
28-
? _createCucumberDataTable(lines: lines, index: index)
29-
: _createDataTableFromExamples(lines: lines, index: index);
28+
isDataTableExamples(lines: lines, index: index)
29+
? _createDataTableFromExamples(lines: lines, index: index)
30+
: _createCucumberDataTable(lines: lines, index: index);
3031
yield* table;
3132
// skip the parsed table
3233
while (isTable(lines: lines, index: index + 1)) {
@@ -46,6 +47,39 @@ bool isTable({
4647
bool hasExamplesFormat({required BddLine bddLine}) =>
4748
examplesRegExp.firstMatch(bddLine.rawLine) != null;
4849

50+
/// Determines if the data table following the step at [index]
51+
/// is intended to be used as Examples (parameter expansion) rather than
52+
/// as a cucumber data table argument.
53+
///
54+
/// Heuristic: if the step contains placeholders like `<name>` and the first
55+
/// row of the following table contains headers that include ALL of those
56+
/// placeholder names, then treat the table as Examples; otherwise, treat it
57+
/// as a cucumber data table.
58+
bool isDataTableExamples({
59+
required List<BddLine> lines,
60+
required int index,
61+
}) {
62+
final step = lines[index];
63+
if (!hasExamplesFormat(bddLine: step)) return false;
64+
final nextIsTable = isTable(lines: lines, index: index + 1);
65+
if (!nextIsTable) return false;
66+
67+
final placeholders =
68+
examplesRegExp
69+
.allMatches(step.rawLine)
70+
.map((m) => m.group(1)!.trim())
71+
.where((e) => e.isNotEmpty)
72+
.toSet();
73+
if (placeholders.isEmpty) return false;
74+
75+
// Use the first table row as headers
76+
final headers = _createRow(bddLine: lines[index + 1]).toSet();
77+
if (headers.isEmpty) return false;
78+
79+
// Only treat as examples when all placeholders are present in headers
80+
return placeholders.every(headers.contains);
81+
}
82+
4983
List<String> _createRow({
5084
required BddLine bddLine,
5185
}) =>

lib/src/feature_file.dart

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,25 +92,27 @@ class FeatureFile {
9292
List<StepFile> getStepFiles() => _stepFiles;
9393

9494
static List<BddLine> _prepareLines(Iterable<BddLine> input) {
95-
final lines = input
95+
final inputList = input.toList(growable: false);
96+
final lines = inputList
9697
.mapIndexed(
9798
(index, bddLine) {
9899
final isStep = bddLine.type == LineType.step;
99-
final hasExamplesFormat = data_table_parser.hasExamplesFormat(
100-
bddLine: bddLine,
101-
);
102100
final isNextTable = data_table_parser.isTable(
103-
lines: input.toList(),
101+
lines: inputList,
104102
index: index + 1,
105103
);
106-
if (isStep && !hasExamplesFormat && isNextTable) {
107-
return BddLine.fromRawValue(
108-
LineType.dataTableStep,
109-
bddLine.rawLine,
110-
);
111-
} else {
112-
return bddLine;
113-
}
104+
final isExamplesTable =
105+
isNextTable &&
106+
data_table_parser.isDataTableExamples(
107+
lines: inputList,
108+
index: index,
109+
);
110+
return isStep && isNextTable && !isExamplesTable
111+
? BddLine.fromRawValue(
112+
LineType.dataTableStep,
113+
bddLine.rawLine,
114+
)
115+
: bddLine;
114116
},
115117
)
116118
.toList(growable: false);

lib/src/scenario_generator.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,28 @@ String _replacePlaceholders(
134134
bool isDataTableStep,
135135
Map<String, String> example,
136136
) {
137+
// For data table steps, we want placeholders in the step text
138+
// to become parameters (wrapped with {}), but placeholders inside the
139+
// DataTable argument should be inlined as raw values.
140+
if (isDataTableStep) {
141+
const marker = '{const bdd.DataTable(';
142+
final dataTableIndex = line.indexOf(marker);
143+
if (dataTableIndex != -1) {
144+
final head = line.substring(0, dataTableIndex);
145+
final tail = line.substring(dataTableIndex);
146+
var headReplaced = head;
147+
var tailReplaced = tail;
148+
for (final e in example.keys) {
149+
headReplaced = headReplaced.replaceAll('<$e>', '{${example[e]}}');
150+
tailReplaced = tailReplaced.replaceAll('<$e>', '${example[e]}');
151+
}
152+
return headReplaced + tailReplaced;
153+
}
154+
}
155+
// Default behaviour: placeholders become parameters
137156
var replaced = line;
138157
for (final e in example.keys) {
139-
final value = isDataTableStep ? '${example[e]}' : '{${example[e]}}';
140-
replaced = replaced.replaceAll('<$e>', value);
158+
replaced = replaced.replaceAll('<$e>', '{${example[e]}}');
141159
}
142160
return replaced;
143161
}

test/data_tables_test.dart

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@ void main() {
5353
Feature: Testing feature
5454
Scenario: Testing scenario
5555
Given the following songs
56-
| artist | title |
56+
| 'artist' | 'title' |
5757
| 'The Beatles' | 'Let It Be' |
5858
| 'Camel' | 'Slow yourself down' |
5959
And the following songs
60-
| artist | title |
60+
| 'artist' | 'title' |
6161
| 'The Beatles' | 'Let It Be' |
6262
| 'Camel' | 'Slow yourself down' |
6363
But the following songs
64-
| artist | title |
64+
| 'artist' | 'title' |
6565
| 'The Beatles' | 'Let It Be' |
6666
| 'Camel' | 'Slow yourself down' |
6767
When the following songs
68-
| artist | title |
68+
| 'artist' | 'title' |
6969
| 'The Beatles' | 'Let It Be' |
7070
| 'Camel' | 'Slow yourself down' |
7171
And I wait
@@ -87,10 +87,10 @@ import './step/i_wait.dart';
8787
void main() {
8888
group(\'\'\'Testing feature\'\'\', () {
8989
testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
90-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
91-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
92-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
93-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
90+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
91+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
92+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
93+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
9494
await iWait(tester);
9595
await theFollowingSongs(tester, const bdd.DataTable([['The Beatles', 'Let It Be']]));
9696
});
@@ -111,7 +111,7 @@ void main() {
111111
Feature: Testing feature
112112
Scenario: Testing scenario
113113
Given the following songs
114-
| artist | title |
114+
| 'artist' | 'title' |
115115
| 'The Beatles' | 'Let It Be' |
116116
| 'Camel' | 'Slow yourself down' |
117117
And I wait
@@ -136,7 +136,7 @@ import './step/the_following_users_exist.dart';
136136
void main() {
137137
group(\'\'\'Testing feature\'\'\', () {
138138
testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
139-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
139+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
140140
await iWait(tester);
141141
await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
142142
await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
@@ -164,7 +164,7 @@ Feature: Testing feature
164164
| 'Flutter' | '@FlutterDev' |
165165
And I wait
166166
And the following songs
167-
| artist | title |
167+
| 'artist' | 'title' |
168168
| 'The Beatles' | 'Let It Be' |
169169
| 'Camel' | 'Slow yourself down' |
170170
''';
@@ -187,7 +187,7 @@ void main() {
187187
await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
188188
await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
189189
await iWait(tester);
190-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
190+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
191191
});
192192
});
193193
}
@@ -213,7 +213,7 @@ Feature: Testing feature
213213
And band <band> is on tour
214214
And I wait
215215
And the following songs
216-
| artist | title |
216+
| 'artist' | 'title' |
217217
| 'The Beatles' | 'Let It Be' |
218218
| 'Camel' | 'Slow yourself down' |
219219
Examples:
@@ -241,7 +241,7 @@ void main() {
241241
await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
242242
await bandIsOnTour(tester, 'Camel');
243243
await iWait(tester);
244-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
244+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
245245
});
246246
});
247247
}
@@ -261,7 +261,7 @@ void main() {
261261
Feature: Testing feature
262262
Scenario Outline: Testing scenario
263263
Given the following songs
264-
| artist | title |
264+
| 'artist' | 'title' |
265265
| 'The Beatles' | 'Let It Be' |
266266
| 'Camel' | 'Slow yourself down' |
267267
And band <band> is on tour
@@ -293,14 +293,14 @@ import './step/the_following_users_exist.dart';
293293
void main() {
294294
group(\'\'\'Testing feature\'\'\', () {
295295
testWidgets(\'\'\'Outline: Testing scenario ('Camel')\'\'\', (tester) async {
296-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
296+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
297297
await bandIsOnTour(tester, 'Camel');
298298
await iWait(tester);
299299
await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
300300
await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
301301
});
302302
testWidgets(\'\'\'Outline: Testing scenario ('Pearl Jam')\'\'\', (tester) async {
303-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
303+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
304304
await bandIsOnTour(tester, 'Pearl Jam');
305305
await iWait(tester);
306306
await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
@@ -326,7 +326,7 @@ Feature: Testing feature
326326
Background:
327327
Given I wait
328328
And the following songs
329-
| artist | title |
329+
| 'artist' | 'title' |
330330
| 'The Beatles' | 'Let It Be' |
331331
| 'Camel' | 'Slow yourself down' |
332332
And I wait
@@ -355,7 +355,7 @@ void main() {
355355
group(\'\'\'Testing feature\'\'\', () {
356356
Future<void> bddSetUp(WidgetTester tester) async {
357357
await iWait(tester);
358-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
358+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
359359
await iWait(tester);
360360
}
361361
testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
@@ -391,7 +391,7 @@ Feature: Testing feature
391391
392392
Scenario: Testing scenario
393393
Given the following songs
394-
| artist | title |
394+
| 'artist' | 'title' |
395395
| 'The Beatles' | 'Let It Be' |
396396
| 'Camel' | 'Slow yourself down' |
397397
@@ -419,7 +419,7 @@ void main() {
419419
}
420420
testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
421421
try {
422-
await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
422+
await theFollowingSongs(tester, const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
423423
} finally {
424424
await bddTearDown(tester);
425425
}
@@ -441,7 +441,7 @@ void main() {
441441
Feature: Testing feature
442442
Scenario: Testing scenario
443443
Given the following {'Good'} songs
444-
| artist | title |
444+
| 'artist' | 'title' |
445445
| 'The Beatles' | 'Let It Be' |
446446
| 'Camel' | 'Slow yourself down' |
447447
''';
@@ -459,7 +459,7 @@ import './step/the_following_songs.dart';
459459
void main() {
460460
group(\'\'\'Testing feature\'\'\', () {
461461
testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
462-
await theFollowingSongs(tester, 'Good', const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
462+
await theFollowingSongs(tester, 'Good', const bdd.DataTable([['artist', 'title'], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
463463
});
464464
});
465465
}
@@ -527,6 +527,47 @@ void main() {
527527
});
528528
});
529529
}
530+
''';
531+
532+
final feature = FeatureFile(
533+
featureDir: 'test.feature',
534+
package: 'test',
535+
input: featureFile,
536+
);
537+
expect(feature.dartContent, expectedFeatureDart);
538+
});
539+
test('Scenario Outline with data table variables in examples', () {
540+
const featureFile = '''
541+
Feature: Testing feature
542+
Scenario Outline: Testing visibility of data table in examples
543+
Given I load the splash screen
544+
Then I verify welcome messages with <authStatus>
545+
| 'text' |
546+
| 'Welcome' |
547+
Examples:
548+
| authStatus |
549+
| 'initial' |
550+
''';
551+
552+
const expectedFeatureDart = '''
553+
// GENERATED CODE - DO NOT MODIFY BY HAND
554+
// ignore_for_file: type=lint, type=warning
555+
556+
import 'package:bdd_widget_test/data_table.dart' as bdd;
557+
import 'package:flutter/material.dart';
558+
import 'package:flutter_test/flutter_test.dart';
559+
560+
import './step/i_load_the_splash_screen.dart';
561+
import './step/i_verify_welcome_messages_with.dart';
562+
563+
void main() {
564+
group(\'\'\'Testing feature\'\'\', () {
565+
testWidgets(\'\'\'Outline: Testing visibility of data table in examples ('initial')\'\'\', (tester) async {
566+
await iLoadTheSplashScreen(tester);
567+
await iVerifyWelcomeMessagesWith(tester, 'initial', const bdd.DataTable([['text'], ['Welcome']]));
568+
});
569+
});
570+
}
530571
''';
531572

532573
final feature = FeatureFile(

0 commit comments

Comments
 (0)