diff --git a/.gitignore b/.gitignore index 807b0654..ed1c215b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,4 @@ testenv/ experiments experiments/* .codex-logs/ -.codex-dev-web.log -.codex-dev-web-*.stdout.log -.codex-dev-web-*.stderr.log +.codex-dev-web*.log diff --git a/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js b/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js index f9d93d5a..0c678ea8 100644 --- a/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js +++ b/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js @@ -37,6 +37,35 @@ test.describe('Generator Schema Editing', () => { expectNoPageErrors(pageErrors); }); + test('tabs through schema row fields before row action buttons', async ({ page }) => { + const { generatorPage, pageErrors } = await openGenerator(page); + + await generatorPage.schema.setTextMode(false); + const row = generatorPage.schema.row(0); + const nameInput = row.locator('input[data-field="name"]'); + const sourceTypeSelect = row.locator('select[data-field="sourceType"]'); + const helpLink = row.locator('[data-field="faker-doc-link"]'); + const valueInput = row.locator('input[data-field="value"]'); + const dragButton = row.locator('[data-action="drag"]'); + + await nameInput.focus(); + await expect(nameInput).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(sourceTypeSelect).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(helpLink).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(valueInput).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(dragButton).toBeFocused(); + + expectNoPageErrors(pageErrors); + }); + test('can edit as schema then edit as text', async ({ page }) => { const { generatorPage, pageErrors } = await openGenerator(page); diff --git a/apps/web/styles.css b/apps/web/styles.css index 2836c3a9..d13d2a43 100644 --- a/apps/web/styles.css +++ b/apps/web/styles.css @@ -2095,10 +2095,47 @@ body.theme-dark .generator-status-text[data-severity='info'] { } .shared-schema-row-actions { + grid-column: 1; + grid-row: 1; display: flex; gap: 0.2rem; } +.shared-schema-row > input[data-field='name'] { + grid-column: 2; + grid-row: 1; +} + +.shared-schema-row > select[data-field='sourceType'] { + grid-column: 3; + grid-row: 1; +} + +.shared-schema-row > .shared-schema-command-picker-control { + grid-column: 4; + grid-row: 1; +} + +.shared-schema-row-command > .shared-schema-help-link { + grid-column: 5; + grid-row: 1; +} + +.shared-schema-row-value > .shared-schema-help-link { + grid-column: 4; + grid-row: 1; +} + +.shared-schema-row > .shared-schema-params-control { + grid-column: 6; + grid-row: 1; +} + +.shared-schema-row > input[data-field='value'] { + grid-column: 5; + grid-row: 1; +} + .shared-schema-drag-handle { cursor: grab; } @@ -2279,9 +2316,11 @@ body.theme-dark .shared-schema-row-validation { } .shared-schema-row-actions { grid-column: 1 / span 3; + grid-row: 1; } .shared-schema-row > input[data-field='name'] { grid-column: 1 / span 3; + grid-row: 2; } .shared-schema-row > select[data-field='sourceType'] { grid-column: 1; diff --git a/docs-src/blog/2026-06-12-release-prep-combinatorial-grid-workflows.md b/docs-src/blog/2026-06-12-combinatorial-grid-workflows.md similarity index 71% rename from docs-src/blog/2026-06-12-release-prep-combinatorial-grid-workflows.md rename to docs-src/blog/2026-06-12-combinatorial-grid-workflows.md index 9330bb24..9ba958f4 100644 --- a/docs-src/blog/2026-06-12-release-prep-combinatorial-grid-workflows.md +++ b/docs-src/blog/2026-06-12-combinatorial-grid-workflows.md @@ -1,39 +1,17 @@ slug: combinatorial-grid-workflows authors: [alan] -tags: [release, feature, combinatorial, schema, import, export, ux] +tags: [release, feature, combinatorial, schema, import, export, ux, faker, api, cli, mcp] date: 2026-06-12T10:00 --- The next release is centered on one theme: faster paths from existing data to realistic, constrained, exportable test sets. -This release adds broader combinatorial generation, schema authoring improvements, better import/export controls, and a few high-value grid usability upgrades. +This release adds broader combinatorial generation, schema authoring improvements, safe-by-default Faker helper controls across interfaces, better import/export controls, and a few high-value grid usability upgrades. -## 1. Auto-increment timestamps for deterministic event streams -You can now generate timestamps that move forward one row at a time instead of relying on purely random dates. - -Example: - -```text -CreatedAt -autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds") -``` - -That produces: - -- row 1: `2026-06-12T12:39:23Z` -- row 2: `2026-06-12T12:39:24Z` -- row 3: `2026-06-12T12:39:25Z` - -This is useful for audit logs, event streams, ordered API records, and any test data where time should progress predictably across generated rows. - -Docs: - -- [autoIncrement Domain](/docs/test-data/domain/autoIncrement) - -## 2. N-wise combinatorial generation, not just pairwise +## 1. N-wise combinatorial generation, not just pairwise The biggest addition is that combinatorial generation now goes beyond pairwise. @@ -63,7 +41,7 @@ Docs:  -## 3. Schema constraints with PICT-style `IF ... THEN ...` +## 2. Schema constraints with PICT-style `IF ... THEN ...` Schema constraints make generated combinations more realistic by filtering out invalid rows. @@ -92,7 +70,7 @@ Docs: - [Schema Definition](/docs/test-data/Schema-Definition) -## 4. Grid to Enum Schema for turning existing tables into generators +## 3. Grid to Enum Schema for turning existing tables into generators If you already have representative data in the main grid, you can now turn that grid into an enum schema automatically. @@ -124,32 +102,9 @@ Docs:  -## 5. Constraint-aware auto-increment sequences for generated identifiers - -Schemas can now generate sequential IDs through the domain model with `autoIncrement.sequence`. -Example: - -```text -Filename -autoIncrement.sequence(start=1, step=5, prefix="filename", suffix=".txt", zeropadding=3) -``` -Generated values: - -```text -filename001.txt -filename0006.txt -filename011.txt -``` - -This is especially useful for ticket IDs, filenames, and human-readable references because the sequence only advances when a row is accepted. If a generated row is rejected by constraints and retried, the skipped attempt does not consume the next number. - -Docs: - -- [Auto Increment Sequences](/docs/test-data/auto-increment-sequences) - -## 6. PICT-style inline enum definitions such as `Name: values` +## 4. PICT-style inline enum definitions such as `Name: values` Schema text now fits more naturally with compact PICT-style authoring. @@ -174,7 +129,8 @@ Docs: - [Schema Definition](/docs/test-data/Schema-Definition) -## 7. Import trimming controls for cleaner amend and import workflows + +## 5. Import trimming controls for cleaner amend and import workflows Imported files and clipboard data can now be normalized during import. @@ -197,7 +153,7 @@ Docs:  -## 8. File export settings for line endings and BOM +## 6. File export settings for line endings and BOM Downloads now support file transport settings without changing the preview text shown in the browser. @@ -214,7 +170,7 @@ Docs:  -## 9. Right-click context menu in the main data grid +## 7. Right-click context menu in the main data grid The editable grid now has a right-click context menu for common grid actions. @@ -224,7 +180,7 @@ Docs: - [Data Grid Editable](/docs/test-data/data-grid-editable) -## 10. Always-visible total row counts in the data grid +## 8. Always-visible total row counts in the data grid The main grid now shows total row counts, and filtered views also show how many rows remain visible. @@ -251,7 +207,8 @@ Taken together, these features make the tool better at moving through the whole 1. start with imported or hand-edited data 2. convert it into a schema 3. add constraints -4. generate the right amount of combinatorial coverage -5. export in the format and file encoding you actually need +4. enable unsafe Faker helper expressions only when the schema is trusted +5. generate the right amount of combinatorial coverage +6. export in the format and file encoding you actually need The release adds more generation power, reduces setup time, and cleanup time around that generation. diff --git a/docs-src/blog/2026-06-30-increments-and-helpers.md b/docs-src/blog/2026-06-30-increments-and-helpers.md new file mode 100644 index 00000000..c51746cf --- /dev/null +++ b/docs-src/blog/2026-06-30-increments-and-helpers.md @@ -0,0 +1,89 @@ +--- +slug: auto-increments-helpers +authors: [alan] +tags: [release, feature] +date: 2026-06-30T10:00 +--- + +This release expands the commands available in the domain model, allows more helper functions in the UI and has general UI improvements. + + + +## 1. Auto-increment timestamps for deterministic event streams + +You can now generate timestamps that move forward one row at a time instead of relying on purely random dates. + +Example: + +```text +CreatedAt +autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds") +``` + +That produces: + +- row 1: `2026-06-12T12:39:23Z` +- row 2: `2026-06-12T12:39:24Z` +- row 3: `2026-06-12T12:39:25Z` + +This is useful for audit logs, event streams, ordered API records, and any test data where time should progress predictably across generated rows. + +Docs: + +- [autoIncrement Domain](/docs/test-data/domain/autoIncrement) + + +## 2. Constraint-aware auto-increment sequences for generated identifiers + +Schemas can now generate sequential IDs through the domain model with `autoIncrement.sequence`. + +Example: + +```text +Filename +autoIncrement.sequence(start=1, step=5, prefix="filename", suffix=".txt", zeropadding=3) +``` + +Generated values: + +```text +filename001.txt +filename006.txt +filename011.txt +``` + +This is especially useful for ticket IDs, filenames, and human-readable references because the sequence only advances when a row is accepted. If a generated row is rejected by constraints and retried, the skipped attempt does not consume the next number. + +Docs: + +- [Auto Increment Sequences](/docs/test-data/auto-increment-sequences) + +## 3. Allow unsafe Faker helpers from the UI when you trust the schema + +Faker helper commands stay safe by default. Simple literal helper calls continue to work normally, but expression-style helper arguments that execute callback-shaped schema text require an explicit opt-in. + +That opt-in is now available in the Web UI as **allow unsafe faker** in the Test Data Settings dialog behind the cog. + +Example unsafe helper: + +```text +Names +helpers.multiple(() => this.person.firstName(), { count: 3 }) +``` + +Use this only for schemas you trust. The browser setting now matches the other integration surfaces: + +- Web UI: enable **allow unsafe faker** from the Test Data Settings cog. +- REST API: pass `unsafeFakerExpressions: true` in supported generation request bodies. +- CLI: pass `--unsafe-faker-expressions`. +- MCP: pass `unsafeFakerExpressions: true` in the generation tool arguments. + +Docs: + +- [Faker Helpers](/docs/test-data/faker/helpers) +- [Web UI](/docs/interfaces-and-deployment/web-ui) +- [REST API](/docs/interfaces-and-deployment/rest-api) +- [MCP](/docs/interfaces-and-deployment/mcp) +- [CLI](/docs/interfaces-and-deployment/cli-node-and-bun) + + diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js b/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js index dcbdc331..9e1bd34b 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js @@ -5,7 +5,7 @@ function captureActiveFieldState(documentObj) { const fieldName = activeElement?.getAttribute?.('data-field'); const actionName = activeElement?.getAttribute?.('data-action'); const rowId = activeElement?.closest?.(SHARED_SCHEMA_ROW_SELECTOR)?.getAttribute?.('data-row-id'); - if (!rowId || (!fieldName && actionName !== 'pick-command')) { + if (!rowId || (!fieldName && !actionName)) { return null; } return { diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js index 31d8aad6..f28af46b 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js @@ -322,6 +322,16 @@ function createSharedSchemaEditorController({ semanticValidationTimers.set(rowId, timerId); }; + const scheduleFocusSettledSemanticValidationForRow = (rowId) => { + clearSemanticValidationTimer(rowId); + if (typeof timerApi?.setTimeout !== 'function') { + applySemanticValidationForRow(rowId); + return; + } + const timerId = timerApi.setTimeout(() => applySemanticValidationForRow(rowId), 0); + semanticValidationTimers.set(rowId, timerId); + }; + const updateTextElementFromRows = () => { const textElement = getTextElement(); if (textElement) { @@ -794,7 +804,7 @@ function createSharedSchemaEditorController({ if (!rowId) { return; } - scheduleSemanticValidationForRow(rowId, { immediate: true }); + scheduleFocusSettledSemanticValidationForRow(rowId); }; const handleClick = async (event) => { diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js index 054df237..02ec89d8 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js @@ -88,13 +88,6 @@ function renderSharedSchemaRows({ rowElem.setAttribute('data-row-id', row.id); rowElem.setAttribute('data-row-index', String(index)); rowElem.innerHTML = ` -