Skip to content

Commit 36f5ed4

Browse files
authored
Merge pull request #75 from eviltester/48-allow-comments-in-schema
comments and spaces in schema
2 parents 15b41a7 + 18a9ffb commit 36f5ed4

19 files changed

Lines changed: 654 additions & 78 deletions

File tree

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,19 @@ You can then create a new table, or amend the existing table or selected rows.
6464

6565
The spec is a paragraph of text where each line is either a 'name' or a 'rule':
6666

67+
### Schema Formatting
68+
69+
- **Comments**: lines starting with `#` (optionally prefixed by whitespace) are treated as comments.
70+
- **Blank lines**: blank lines are allowed and ignored, so you can separate column groups for readability.
71+
- **Column definitions**: each column is defined as `name` followed by `rule` on the next logical content line.
72+
6773
```
74+
# optional comment
75+
6876
name
6977
rule
78+
79+
# another comment
7080
name
7181
rule
7282
```
@@ -102,12 +112,19 @@ The `fake` method is also supported, which takes a mustache template style strin
102112
So a sample test data spec might look like:
103113

104114
```
115+
# person details
105116
name
106117
helpers.fake("{{name.lastName}}, {{name.firstName}}")
118+
119+
# profile text
107120
desc
108121
faker.lorem.paragraph
122+
123+
# preference data
109124
collects
110125
hacker.noun
126+
127+
# regex example
111128
prefers
112129
(Connie|Bob)
113130
```
@@ -119,10 +136,15 @@ When you have 2 or more enum fields (comma-separated values), you can generate p
119136
For enum data, use comma-separated values in your spec:
120137

121138
```
139+
# pairwise parameters
122140
browser
123141
chrome,firefox,safari,edge
142+
143+
# viewport class
124144
device
125145
desktop,tablet,mobile
146+
147+
# style variant
126148
theme
127149
light,dark
128150
```
@@ -337,7 +359,7 @@ Example request body:
337359

338360
```json
339361
{
340-
"textSpec": "Name\nBob",
362+
"textSpec": "# literal example\\n\\nName\\nBob",
341363
"rowCount": 3,
342364
"outputFormat": "json"
343365
}
@@ -368,7 +390,7 @@ Add `unsafeFakerExpressions: true` to individual requests:
368390

369391
```json
370392
{
371-
"textSpec": "Name\nperson.firstName\nScore\nnumber.int({\"min\": 18, \"max\": 65})",
393+
"textSpec": "# faker + numeric range\\n\\nName\\nperson.firstName\\n\\nScore\\nnumber.int({\"min\": 18, \"max\": 65})",
372394
"rowCount": 5,
373395
"outputFormat": "json",
374396
"unsafeFakerExpressions": true

apps/api/src/fromschema.route.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,16 @@ test('/v1/generate/fromschema supports pairwise query flag', async () => {
176176
['Safari', 'Dark'],
177177
]);
178178
});
179+
180+
test('/v1/generate/fromschema accepts comments and blank lines in schema text', async () => {
181+
const response = await fetch(url('/v1/generate/fromschema?rowCount=2&outputFormat=json'), {
182+
method: 'POST',
183+
headers: { 'content-type': 'text/plain' },
184+
body: '# skip me\n\nPriority\nenum(high,medium,low)\n\n# and me\nStatus\nenum(active,inactive,pending)',
185+
});
186+
187+
expect(response.status).toBe(200);
188+
const body = await response.json();
189+
expect(body.headers).toEqual(['Priority', 'Status']);
190+
expect(body.rows).toHaveLength(2);
191+
});

apps/cli/src/tests/run-cli.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,27 @@ test('generates deterministic pairwise output in buffered mode', async () => {
177177
{ Browser: 'Safari', Theme: 'Dark' },
178178
]);
179179
});
180+
181+
test('supports comments and blank lines in input schema', async () => {
182+
const platform = makePlatform({
183+
textSpec: '# comment\n\nPriority\nenum(high,medium,low)\n\nStatus\nenum(active,inactive,pending)',
184+
});
185+
const code = await runCliCommand({
186+
platform,
187+
options: {
188+
inputFile: 'spec.txt',
189+
outputFile: null,
190+
format: 'json',
191+
rowCount: 2,
192+
testMode: false,
193+
showProgress: false,
194+
shouldStream: false,
195+
unsafeFakerExpressions: false,
196+
pairwise: false,
197+
},
198+
});
199+
expect(code).toBe(0);
200+
const parsed = JSON.parse(platform.out.join('').trim());
201+
expect(parsed).toHaveLength(2);
202+
expect(Object.keys(parsed[0])).toEqual(['Priority', 'Status']);
203+
});

apps/mcp/src/mcp.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ test('MCP server handles generate_data_from_spec tool call', () => {
4949
expect(response?.result?.structuredContent?.ok).toBe(true);
5050
});
5151

52+
test('MCP server accepts comments and blank lines in textSpec', () => {
53+
const response = requestServer({
54+
jsonrpc: '2.0',
55+
id: 201,
56+
method: 'tools/call',
57+
params: {
58+
name: 'generate_data_from_spec',
59+
arguments: {
60+
textSpec: '# comment\n\nPriority\nenum(high,medium,low)\nStatus\nenum(active,inactive,pending)',
61+
rowCount: 1,
62+
outputFormat: 'json',
63+
},
64+
},
65+
});
66+
const payload = JSON.parse(response?.result?.content?.[0]?.text || '{}');
67+
expect(payload.ok).toBe(true);
68+
expect(payload.headers).toEqual(['Priority', 'Status']);
69+
expect(payload.rows).toHaveLength(1);
70+
});
71+
5272
test('MCP server supports pairwise generation', () => {
5373
const response = requestServer({
5474
jsonrpc: '2.0',

apps/web/src/tests/browser/planned-functional/test-data/text-schema.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ test.describe('7. Test Data Generation', () => {
99
await appPage.testDataPanel.expectExpanded();
1010
const beforeSchema = await appPage.testDataPanel.getSchemaRowCount();
1111

12-
await appPage.testDataPanel.addSchemaRow();
13-
await expect.poll(async () => appPage.testDataPanel.getSchemaRowCount()).toBe(beforeSchema + 1);
14-
15-
const schemaRowIndex = beforeSchema;
16-
await appPage.testDataPanel.setSchemaCell(schemaRowIndex, 'columnName', 'First Name');
17-
await appPage.testDataPanel.setSchemaTypeValue(schemaRowIndex, 'faker');
18-
await appPage.testDataPanel.setSchemaCell(schemaRowIndex, 'value', 'faker.person.firstName');
12+
await appPage.testDataPanel.setSchemaText(
13+
'# this comment should be ignored\n\nFirst Name\nperson.firstName\n\n# second comment\nStatus\nenum(active,inactive,pending)'
14+
);
15+
await expect.poll(async () => appPage.testDataPanel.getSchemaRowCount()).toBe(beforeSchema + 2);
1916
await appPage.testDataPanel.setGenerateCount(5);
2017

2118
await appPage.testDataPanel.clickGenerate();
2219
await expect.poll(async () => appPage.gridEditor.renderer.countRows()).toBe(5);
2320
await expect.poll(async () => appPage.gridEditor.header.getColumnNames()).toContain('First Name');
21+
await expect.poll(async () => appPage.gridEditor.header.getColumnNames()).toContain('Status');
22+
// comments are not rendered as schema rows
23+
expect(await appPage.testDataPanel.getSchemaRowCount()).toBe(beforeSchema + 2);
2424

2525
const values = await appPage.gridEditor.renderer.getColumnTextsByName('First Name');
2626
expect(values.filter(Boolean).length).toBe(5);

docs-src/docs/040-test-data/040-literal-test-data.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Schema text format uses:
3131
For a literal column:
3232

3333
```text
34+
# deployment context
3435
Environment
3536
UAT
3637
```
@@ -42,10 +43,15 @@ If you generate 5 rows, every row in `Environment` will be `UAT`.
4243
You can define several static columns at once:
4344

4445
```text
46+
# locale defaults
4547
Country
4648
UK
49+
50+
# currency defaults
4751
Currency
4852
GBP
53+
54+
# workflow state
4955
Status
5056
ACTIVE
5157
```
@@ -57,12 +63,19 @@ This is useful for creating baseline datasets quickly.
5763
Literal values are often combined with Faker and Regex columns.
5864

5965
```text
66+
# constant label
6067
Environment
6168
UAT
69+
70+
# generated identity
6271
Customer Name
6372
person.fullName
73+
74+
# generated order id
6475
Order Ref
6576
[A-Z]{3}-[0-9]{6}
77+
78+
# constant flag
6679
Is Premium
6780
true
6881
```

docs-src/docs/040-test-data/050-pairwise-testing.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,28 @@ Pairwise testing is most effective when:
3131

3232
## How to Generate Pairwise Data
3333

34+
## Schema Formatting
35+
36+
Schema text supports:
37+
38+
- **Comments**: lines starting with `#` (after optional leading whitespace) are treated as comments.
39+
- **Blank lines**: allowed and ignored, useful for readability between column groups.
40+
- **Column definitions**: each column name must be followed by its generation rule on the next logical content line.
41+
3442
### In the Main App (app.html)
3543

3644
1. **Open the Test Data section** by clicking the "Test Data" header
3745
2. **Define your enum parameters** in the Test Data Text Schema:
3846
```
47+
# categorical parameters
3948
color
4049
red,blue,green,yellow
50+
51+
# sizing options
4152
size
4253
small,medium,large
54+
55+
# material choices
4356
material
4457
wood,metal
4558
```
@@ -66,8 +79,11 @@ AnyWayData supports multiple formats for specifying enum values in pairwise test
6679
The basic format uses comma-separated values:
6780

6881
```
82+
# pairwise field 1
6983
Priority
7084
high,medium,low
85+
86+
# pairwise field 2
7187
Status
7288
active,inactive,pending
7389
```
@@ -78,24 +94,33 @@ For more complex scenarios, you can use function-based formats:
7894

7995
#### Basic enum() Function
8096
```
97+
# function style enum
8198
Priority
8299
enum(high,medium,low)
100+
101+
# another function style enum
83102
Status
84103
enum(active,inactive,pending)
85104
```
86105

87106
#### Datatype enum() Function
88107
```
108+
# datatype enum form
89109
Priority
90110
datatype.enum(high,medium,low)
111+
112+
# second datatype enum
91113
Status
92114
datatype.enum(active,inactive,pending)
93115
```
94116

95117
#### Full AWD enum() Function
96118
```
119+
# fully-qualified enum form
97120
Priority
98121
awd.datatype.enum(high,medium,low)
122+
123+
# fully-qualified enum form
99124
Status
100125
awd.datatype.enum(active,inactive,pending)
101126
```
@@ -105,10 +130,15 @@ awd.datatype.enum(active,inactive,pending)
105130
When your enum values contain commas or special characters, use quoted values:
106131

107132
```
133+
# values containing commas
108134
Product Category
109135
enum("Hardware, Electronics","Software, Applications","Books, Media")
136+
137+
# geo values containing commas
110138
Location
111139
enum("New York, NY","Los Angeles, CA","Chicago, IL")
140+
141+
# status text with commas
112142
Status Message
113143
enum("Ready, waiting for input","Processing, please wait","Error, check configuration")
114144
```
@@ -118,8 +148,11 @@ enum("Ready, waiting for input","Processing, please wait","Error, check configur
118148
You can mix quoted and unquoted values in the same enum:
119149

120150
```
151+
# mixed quoting example
121152
Environment
122153
enum("Production, Live",staging,development,"Test, QA")
154+
155+
# mixed quoting example
123156
User Type
124157
enum("Admin, Full Access",editor,viewer,"Guest, Limited")
125158
```
@@ -136,10 +169,15 @@ Consider testing an API with both categorical parameters (that need pairwise cov
136169
- Then random values User ID, etc.
137170

138171
```
172+
# pairwise enums
139173
HTTP Method
140174
enum(GET,POST,PUT,DELETE)
175+
176+
# pairwise enums
141177
Content Type
142178
enum("application/json","application/xml","text/plain")
179+
180+
# randomized fields
143181
User ID
144182
faker.number.int
145183
Email Address
@@ -200,8 +238,11 @@ AnyWayData uses a greedy set cover approximation algorithm that:
200238
Example Input Schema:
201239

202240
```csv
241+
# simple pairwise schema
203242
color
204243
red,blue,green,yellow
244+
245+
# simple pairwise schema
205246
size
206247
small,medium,large
207248
```

docs-src/docs/070-interfaces-and-deployment/030-rest-api.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ Use endpoints at `http://localhost:8082`.
137137

138138
Both endpoints generate data from the same schema language and output formats. The key difference is request shape and content type, not generation capability.
139139

140+
## Schema Formatting
141+
142+
Schema text supports:
143+
144+
- **Comments**: lines starting with `#` (after optional leading whitespace) are treated as comments.
145+
- **Blank lines**: allowed and ignored, useful for readability between column groups.
146+
- **Column definitions**: each column name must be followed by its generation rule on the next logical content line.
147+
140148
## API Examples
141149

142150
Health check:
@@ -151,7 +159,7 @@ Generate JSON output with a JSON payload:
151159
curl -X POST http://localhost:3000/v1/generate \
152160
-H "Content-Type: application/json" \
153161
-d '{
154-
"textSpec": "Name\nBob\n\nCity\nlondon",
162+
"textSpec": "# literal values\n\nName\nBob\n\n# lower-case city\nCity\nlondon",
155163
"rowCount": 3,
156164
"outputFormat": "json"
157165
}'
@@ -163,7 +171,7 @@ Generate CSV output with CSV-specific options:
163171
curl -X POST http://localhost:3000/v1/generate \
164172
-H "Content-Type: application/json" \
165173
-d '{
166-
"textSpec": "Name\nBob",
174+
"textSpec": "# basic csv schema\n\nName\nBob",
167175
"rowCount": 2,
168176
"outputFormat": "csv",
169177
"options": {
@@ -180,7 +188,7 @@ Generate using raw schema text (`fromschema`):
180188
```bash
181189
curl -X POST "http://localhost:3000/v1/generate/fromschema?outputFormat=markdown&rowCount=2" \
182190
-H "Content-Type: text/plain" \
183-
--data-binary $'Name\nBob\n\nId\n1'
191+
--data-binary $'# markdown sample\n\nName\nBob\n\n# numeric id\nId\n1'
184192
```
185193

186194
Get current options for a format:

0 commit comments

Comments
 (0)