Skip to content

Commit 9efb5e7

Browse files
committed
Address enum schema review feedback
1 parent 23e1f02 commit 9efb5e7

7 files changed

Lines changed: 113 additions & 12 deletions

File tree

docs-src/docs/040-test-data/015-data-grid-editable.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The default value is:
6060
- the largest column unique-value count when it is `256` or less
6161
- `256` when the largest column has more than `256` unique values
6262

63-
If the chosen limit is lower than the number of unique values in one or more columns then AnyWayData will ask for confirmation before truncating the schema. Truncation keeps the first values seen in current grid row order.
63+
If the chosen limit is lower than the number of unique values in one or more columns, then AnyWayData will ask for confirmation before truncating the schema. Truncation keeps the first values seen in current grid row order.
6464

6565
When accepted, the generated enum schema replaces the existing schema definition in the Test Data editor. The schema grid, schema text area, validation state, and pairwise button visibility are all refreshed automatically.
6666

@@ -137,4 +137,3 @@ The schema in the Column Definition Data Grid will be used to generate the data.
137137

138138
All data generation happens in the browser so the amount of data you can generate is limited only by the performance and memory of your computer.
139139

140-

packages/core-ui/js/gui_components/app/test-data-grid/controller/test-data-grid-controller.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,13 @@ function createTestDataGenerationPanelManager({
215215
return false;
216216
}
217217

218-
state.dataPopulationPanel?.setGenerateSchemaBusy?.(true);
219-
statusServiceApi.setTestDataLoadingStatus('Scanning grid for enum schema...');
220-
221218
try {
219+
state.dataPopulationPanel?.setGenerateSchemaBusy?.(true);
220+
statusServiceApi.setTestDataLoadingStatus('Scanning grid for enum schema...');
222221
const dataTable = await Promise.resolve(gridExtras.getGridAsGenericDataTable());
223222
const initialSummary = createEnumSchemaRowsFromGrid({
224223
dataTable,
225224
maxEnumValues: DEFAULT_ENUM_LIMIT,
226-
createBlankRow,
227225
});
228226

229227
if (initialSummary.usableColumns.length === 0) {

packages/core-ui/js/gui_components/app/test-data-grid/schema/grid-to-enum-schema.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ function normaliseGridCellValue(value) {
1414
return String(value ?? '').trim();
1515
}
1616

17+
function serialiseEnumSchemaValue(value) {
18+
const normalised = normaliseGridCellValue(value);
19+
if (!/[,"\r\n]/.test(normalised)) {
20+
return normalised;
21+
}
22+
return `"${normalised.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
23+
}
24+
1725
function buildGridEnumSchemaSummary({ dataTable, maxEnumValues = DEFAULT_ENUM_LIMIT } = {}) {
1826
const headers = Array.isArray(dataTable?.getHeaders?.()) ? dataTable.getHeaders() : [];
1927
const rowCount = Number.parseInt(dataTable?.getRowCount?.() ?? 0, 10) || 0;
@@ -64,7 +72,7 @@ function createEnumSchemaRowsFromGrid({ dataTable, maxEnumValues = DEFAULT_ENUM_
6472
sourceType: SOURCE_TYPE_ENUM,
6573
command: '',
6674
params: '',
67-
value: column.keptValues.join(','),
75+
value: column.keptValues.map(serialiseEnumSchemaValue).join(','),
6876
comments: '',
6977
leadingTextLines: [],
7078
}));

packages/core-ui/js/gui_components/shared/modal-text-input.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ function createTextInputDialogComponent({
7676
const okButton = backdrop.querySelector('[data-role="text-input-dialog-ok"]');
7777
const cancelButton = backdrop.querySelector('[data-role="text-input-dialog-cancel"]');
7878

79-
titleElem.textContent = String(title || 'Enter Value');
80-
const messageText = String(message || '');
79+
titleElem.textContent = String(title ?? 'Enter Value');
80+
const messageText = String(message ?? '');
8181
messageElem.textContent = messageText;
8282
messageElem.hidden = messageText.length === 0;
83-
const labelText = String(label || '');
83+
const labelText = String(label ?? '');
8484
labelElem.textContent = labelText;
8585
labelElem.hidden = labelText.length === 0;
8686
inputElem.value = String(initialValue ?? '');

packages/core-ui/src/tests/grid/controller/test-data-grid-controller.test.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,76 @@ describe('test data grid controller', () => {
648648
);
649649
expect(panel.replaceSchemaRows).not.toHaveBeenCalled();
650650
});
651+
652+
test('resets the schema busy flag when loading status throws during grid-to-enum startup', async () => {
653+
const panel = {
654+
destroy: jest.fn(),
655+
getMode: jest.fn(() => 'new-table'),
656+
setPairwiseVisible: jest.fn(),
657+
setRowCountValue: jest.fn(),
658+
setGenerateSchemaBusy: jest.fn(),
659+
validateSchemaRows: jest.fn(() => ({ rows: [], errors: [] })),
660+
syncSchemaTextFromRows: jest.fn(),
661+
insertSampleSchema: jest.fn(),
662+
getSchemaDefinition: jest.fn(() => ({ replaceRows: jest.fn() })),
663+
getState: jest.fn(() => ({
664+
schemaDefinitionProps: {
665+
createBlankRow: () => ({
666+
id: 'generated-row',
667+
name: '',
668+
sourceType: 'regex',
669+
command: '',
670+
params: '',
671+
value: '',
672+
comments: '',
673+
leadingTextLines: [],
674+
}),
675+
},
676+
})),
677+
replaceSchemaRows: jest.fn(() => ({ rows: [], errors: [] })),
678+
};
679+
const createDataPopulationPanelComponentFn = jest.fn(({ callbacks }) => {
680+
panel.callbacks = callbacks;
681+
return panel;
682+
});
683+
const loadingError = new Error('status torn down');
684+
685+
const control = createTestDataGenerationPanelManager({
686+
documentObj: document,
687+
initializeSchemaErrorDisplayFn: jest.fn(),
688+
identifyFakerCommandsFn: jest.fn(),
689+
createTestDataGenerationServiceFn: jest.fn(() => ({
690+
updatePairwiseButtonVisibility: jest.fn(),
691+
generateTestData: jest.fn(),
692+
generatePairwiseTestData: jest.fn(),
693+
})),
694+
createDataPopulationPanelComponentFn,
695+
createTestDataUiStatusServiceFn: jest.fn(() => ({
696+
setTestDataStatus: jest.fn(),
697+
setTestDataLoadingStatus: jest.fn(() => {
698+
throw loadingError;
699+
}),
700+
clearPendingTestDataStatusReset: jest.fn(),
701+
scheduleTestDataStatusReset: jest.fn(),
702+
destroy: jest.fn(),
703+
})),
704+
createCombinationsDialogComponentFn: jest.fn(() => ({ destroy: jest.fn() })),
705+
});
706+
707+
control.mountTestDataGenerationPanel(
708+
'host',
709+
{},
710+
{},
711+
{
712+
getGridAsGenericDataTable: jest.fn(() => ({
713+
getHeaders: () => ['Status'],
714+
getRowCount: () => 1,
715+
getCell: () => 'active',
716+
})),
717+
}
718+
);
719+
720+
await expect(panel.callbacks.onGenerateSchemaFromGrid()).rejects.toThrow(loadingError);
721+
expect(panel.setGenerateSchemaBusy.mock.calls).toEqual([[true], [false]]);
722+
});
651723
});

packages/core-ui/src/tests/grid/schema/grid-to-enum-schema.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,24 @@ describe('grid-to-enum-schema', () => {
7474
}),
7575
]);
7676
});
77+
78+
test('quotes enum values that contain commas so they stay single choices', () => {
79+
const table = createTable({
80+
headers: ['City'],
81+
rows: [['New York, NY'], ['London'], ['New York, NY']],
82+
});
83+
84+
const result = createEnumSchemaRowsFromGrid({
85+
dataTable: table,
86+
createBlankRow: () => ({ id: 'row', sourceType: 'regex', value: 'seed' }),
87+
});
88+
89+
expect(result.rows).toEqual([
90+
expect.objectContaining({
91+
name: 'City',
92+
sourceType: 'enum',
93+
value: '"New York, NY",London',
94+
}),
95+
]);
96+
});
7797
});

packages/core/js/data_generation/utils/enumParser.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
* Handles both simple comma-separated and function-based enum formats
44
*/
55
export class EnumParser {
6+
static unescapeQuotedEnumValue(value) {
7+
return String(value ?? '').replace(/\\(["\\])/g, '$1');
8+
}
9+
610
static isShorthandEnumFormat(ruleSpec) {
711
return /^enum\s+/.test(String(ruleSpec || '').trim());
812
}
@@ -51,7 +55,7 @@ export class EnumParser {
5155
.map((v) => {
5256
const trimmed = v.trim();
5357
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
54-
return trimmed.slice(1, -1);
58+
return this.unescapeQuotedEnumValue(trimmed.slice(1, -1));
5559
}
5660
return trimmed;
5761
});
@@ -108,7 +112,7 @@ export class EnumParser {
108112
return values.map((value) => {
109113
const trimmed = value.trim();
110114
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
111-
return trimmed.slice(1, -1);
115+
return this.unescapeQuotedEnumValue(trimmed.slice(1, -1));
112116
}
113117
return trimmed;
114118
});

0 commit comments

Comments
 (0)