Skip to content

Commit c33fe92

Browse files
Copilothawkeyexl
andcommitted
Complete schema reference builder enhancement for v3 schemas
Co-authored-by: hawkeyexl <[email protected]>
1 parent 423f23d commit c33fe92

File tree

19 files changed

+282
-147
lines changed

19 files changed

+282
-147
lines changed

.scripts/buildSchemaReferences.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
const fs = require("fs");
2-
const path = require("path");
3-
const parser = require("@apidevtools/json-schema-ref-parser");
4-
const { schemas } = require("doc-detective-common");
5-
const { exit } = require("process");
1+
/*
2+
This is a legacy version of the schema reference builder.
3+
Please use the buildSchemaReferencesV3.js script instead, which has better support for v3 schemas.
4+
*/
65

7-
main();
8-
9-
// TODO: Add `minimum` values
10-
// TODO: Add `maximum` values
6+
// Forward to V3 implementation
7+
require('./buildSchemaReferencesV3.js');
118

129
async function main() {
1310
const schemasToGenerate = [

.scripts/buildSchemaReferencesV3.js

Lines changed: 171 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,38 @@ async function main() {
4040
":-- | :-- | :-- | :--",
4141
];
4242

43+
// Handle top-level anyOf/oneOf schemas (like screenshot which can be string, object, or boolean)
44+
if (schema.anyOf || schema.oneOf) {
45+
const topLevelRows = processTopLevelVariations(schema);
46+
if (topLevelRows.length > 0) {
47+
fields = fields.concat(topLevelRows);
48+
}
49+
}
50+
4351
// Extract all unique top-level property keys, handling direct properties, anyOf/oneOf, and allOf structures
4452
const propertyKeys = extractAllPropertyKeys(schema);
4553

4654
// Process each top-level property
55+
const generatedRows = [];
4756
for (const field of propertyKeys) {
4857
let fieldDetails = parseField(schema, field); // Pass only the top-level field name
49-
fields = fields.concat(fieldDetails);
58+
generatedRows.push(...fieldDetails);
59+
}
60+
61+
// Deduplicate rows by creating a map of property name+type to row
62+
const uniqueRows = {};
63+
for (const row of generatedRows) {
64+
const [name, type, description, defaultValue] = row.split(' | ');
65+
const key = `${name}|${type}`;
66+
67+
// Only add if this property+type combination doesn't exist already
68+
if (!uniqueRows[key]) {
69+
uniqueRows[key] = row;
70+
}
5071
}
72+
73+
// Add the unique rows to fields
74+
fields = fields.concat(Object.values(uniqueRows));
5175
fields.push("");
5276

5377
// Examples
@@ -92,6 +116,69 @@ async function main() {
92116
console.log("References generated.");
93117
}
94118

119+
/**
120+
* Process top-level variations for schemas that can be multiple types
121+
* (e.g., screenshot can be a string, object, or boolean)
122+
*/
123+
function processTopLevelVariations(schema) {
124+
const rows = [];
125+
126+
// Get all variants
127+
const variants = schema.anyOf || schema.oneOf;
128+
129+
for (const variant of variants) {
130+
// Handle direct types
131+
if (variant.type) {
132+
processVariantWithType(schema.title, variant, rows);
133+
}
134+
// Handle nested anyOf/oneOf
135+
else if (variant.anyOf || variant.oneOf) {
136+
const nestedVariants = variant.anyOf || variant.oneOf;
137+
for (const nestedVariant of nestedVariants) {
138+
processVariantWithType(schema.title, nestedVariant, rows);
139+
}
140+
}
141+
}
142+
143+
return rows;
144+
}
145+
146+
/**
147+
* Process a schema variant that has a type
148+
*/
149+
function processVariantWithType(schemaTitle, variant, rows) {
150+
if (variant.type === 'string') {
151+
rows.push(`${schemaTitle} | string | ${variant.description || 'No description provided.'} | `);
152+
}
153+
else if (variant.type === 'boolean') {
154+
rows.push(`${schemaTitle} | boolean | ${variant.description || 'No description provided.'} | `);
155+
}
156+
else if (variant.type === 'number' || variant.type === 'integer') {
157+
const defaultValue = variant.default !== undefined ? `\`${variant.default}\`` : '';
158+
rows.push(`${schemaTitle} | ${variant.type} | ${variant.description || 'No description provided.'} | ${defaultValue}`);
159+
}
160+
else if (variant.type === 'array') {
161+
let arrayType = 'array';
162+
if (variant.items) {
163+
if (variant.items.oneOf || variant.items.anyOf) {
164+
const itemVariants = variant.items.oneOf || variant.items.anyOf;
165+
if (itemVariants.length === 1 && itemVariants[0].type) {
166+
arrayType += ` of ${itemVariants[0].type}`;
167+
} else {
168+
const types = itemVariants.filter(v => v.type).map(v => v.type).join(', ');
169+
if (types) {
170+
arrayType += ` of ${types}`;
171+
}
172+
}
173+
} else if (variant.items.type) {
174+
arrayType += ` of ${variant.items.type}`;
175+
}
176+
}
177+
const defaultValue = variant.default ? `\`\`${JSON.stringify(variant.default)}\`\`` : '';
178+
rows.push(`${schemaTitle} | ${arrayType} | ${variant.description || 'No description provided.'} | ${defaultValue}`);
179+
}
180+
}
181+
95182
/**
96183
* Extract all unique property keys from a schema, including properties in anyOf/oneOf/allOf structures
97184
*/
@@ -108,6 +195,11 @@ function extractAllPropertyKeys(schema) {
108195
const variants = schema.anyOf || schema.oneOf;
109196

110197
for (const variant of variants) {
198+
// Skip non-object variants at the top level
199+
if (variant.type && variant.type !== 'object') {
200+
continue;
201+
}
202+
111203
// Check for properties directly within the variant
112204
if (variant.properties) {
113205
Object.keys(variant.properties).forEach(key => propertyKeys.add(key));
@@ -225,15 +317,44 @@ function extractTypeVariations(property) {
225317
if (variant.type) {
226318
variations.push({
227319
type: variant.type,
228-
property: variant
320+
property: {
321+
...variant,
322+
description: variant.description || property.description
323+
}
229324
});
230325
} else if (variant.title) {
231326
// Reference to another schema
232327
variations.push({
233328
type: 'object',
234-
property: variant,
329+
property: {
330+
...variant,
331+
description: variant.description || property.description
332+
},
235333
title: variant.title
236334
});
335+
} else if (variant.anyOf || variant.oneOf) {
336+
// Handle nested anyOf/oneOf
337+
const nestedVariants = variant.anyOf || variant.oneOf;
338+
for (const nestedVariant of nestedVariants) {
339+
if (nestedVariant.type) {
340+
variations.push({
341+
type: nestedVariant.type,
342+
property: {
343+
...nestedVariant,
344+
description: nestedVariant.description || variant.description || property.description
345+
}
346+
});
347+
} else if (nestedVariant.title) {
348+
variations.push({
349+
type: 'object',
350+
property: {
351+
...nestedVariant,
352+
description: nestedVariant.description || variant.description || property.description
353+
},
354+
title: nestedVariant.title
355+
});
356+
}
357+
}
237358
}
238359
}
239360
}
@@ -342,8 +463,30 @@ function generatePropertyRow(name, property, options = {}) {
342463

343464
// Handle array subtypes if this is an array
344465
if (explicitType === 'array') {
345-
const subTypes = getArraySubTypes(property, 0);
346-
typeDetails.type = typeDetails.type + subTypes;
466+
// Check if items have a specific type
467+
if (property.items && property.items.type) {
468+
typeDetails.type = `${typeDetails.type} of ${property.items.type}`;
469+
}
470+
// Check if items have multiple possible types
471+
else if (property.items && (property.items.anyOf || property.items.oneOf)) {
472+
const itemVariants = property.items.anyOf || property.items.oneOf;
473+
if (itemVariants.length === 1 && itemVariants[0].type) {
474+
typeDetails.type = `${typeDetails.type} of ${itemVariants[0].type}`;
475+
} else {
476+
const types = itemVariants
477+
.filter(v => v.type)
478+
.map(v => v.type)
479+
.join(', ');
480+
if (types) {
481+
typeDetails.type = `${typeDetails.type} of ${types}`;
482+
}
483+
}
484+
}
485+
// If no specific item type information, use getArraySubTypes
486+
else {
487+
const subTypes = getArraySubTypes(property, 0);
488+
typeDetails.type = typeDetails.type + subTypes;
489+
}
347490
}
348491
} else {
349492
typeDetails = getTypes(property);
@@ -367,6 +510,29 @@ function generatePropertyRow(name, property, options = {}) {
367510
description = description + enums;
368511
}
369512

513+
// Add constraint information
514+
const constraints = [];
515+
516+
if (property.minimum !== undefined) {
517+
constraints.push(`Minimum: ${property.minimum}`);
518+
}
519+
if (property.maximum !== undefined) {
520+
constraints.push(`Maximum: ${property.maximum}`);
521+
}
522+
if (property.minLength !== undefined) {
523+
constraints.push(`Minimum length: ${property.minLength}`);
524+
}
525+
if (property.maxLength !== undefined) {
526+
constraints.push(`Maximum length: ${property.maxLength}`);
527+
}
528+
if (property.pattern) {
529+
constraints.push(`Pattern: \`${property.pattern}\``);
530+
}
531+
532+
if (constraints.length > 0) {
533+
description += `<br/><br/>${constraints.join('. ')}`;
534+
}
535+
370536
// Format default value
371537
let defaultValue = formatDefaultValue(property);
372538

docs/references/schemas/checkLink.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77

88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
10-
url | string | Required. URL to check. Can be a full URL or a path. If a path is provided, `origin` must be specified. |
10+
checkLink | string | Check if an HTTP or HTTPS URL returns an acceptable status code from a GET request. |
11+
url | string | Required. URL to check. Can be a full URL or a path. If a path is provided, `origin` must be specified.<br/><br/>Pattern: `(^(http://|https://|/).*|\$[A-Za-z0-9_]+)` |
1112
origin | string | Optional. Protocol and domain to navigate to. Prepended to `url`. |
12-
statusCodes | integer | Optional. No description provided. |
13-
statusCodes | array of integer | Optional. No description provided. |
13+
statusCodes | integer | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. |
14+
statusCodes | array of integer | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. |
1415

1516
## Examples
1617

docs/references/schemas/click.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Click or tap an element.
77

88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
10+
click | string | Display text or selector of the element to find. |
11+
click | boolean | No description provided. |
1012
button | string | Optional. Kind of click to perform.<br/><br/>Accepted values: `left`, `right`, `middle` |
1113
elementText | string | Optional. Display text of the element to click. If combined with `selector`, the element must match both the text and the selector. |
1214
selector | string | Optional. Selector of the element to click. If combined with `elementText`, the element must match both the text and the selector. |

docs/references/schemas/config.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ Configuration options for Doc Detective operations.
88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
1010
configId | string | Optional. Identifier for the configuration. |
11-
input | unknown | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. | `.`
11+
input | string | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. |
12+
input | array of string | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. |
1213
output | string | Optional. Path of the directory in which to store the output of Doc Detective commands. If a file path is specified, Doc Detective attempts to honor the file name specified, but file path behavior is controlled by the configured reporters. | `.`
1314
recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specifications and source files. | `true`
1415
relativePathBase | string | Optional. Whether paths should be interpreted as relative to the current working directory (`cwd`) or to the file in which they're specified (`file`).<br/><br/>Accepted values: `cwd`, `file` | `file`
1516
loadVariables | string | Optional. Load environment variables from the specified `.env` file. |
1617
origin | string | Optional. Default protocol and domain to use for relative URLs. |
17-
beforeAny | string | Optional. No description provided. |
18-
beforeAny | array of string | Optional. No description provided. |
19-
afterAll | string | Optional. No description provided. |
20-
afterAll | array of string | Optional. No description provided. |
18+
beforeAny | string | Optional. Path(s) to test specifications to perform before those specified by `input`. Useful for setting up testing environments. |
19+
beforeAny | array of string | Optional. Path(s) to test specifications to perform before those specified by `input`. Useful for setting up testing environments. |
20+
afterAll | string | Optional. Path(s) to test specifications to perform after those specified by `input`. Useful for cleaning up testing environments. |
21+
afterAll | array of string | Optional. Path(s) to test specifications to perform after those specified by `input`. Useful for cleaning up testing environments. |
2122
detectSteps | boolean | Optional. Whether or not to detect steps in input files based on defined markup. | `true`
2223
logLevel | string | Optional. Amount of detail to output when performing an operation.<br/><br/>Accepted values: `silent`, `error`, `warning`, `info`, `debug` | `info`
23-
runOn | array of object([context](/docs/references/schemas/context)) | Optional. Contexts to run the test in. Overrides contexts defined at the config and spec levels. |
24-
fileTypes | array of <br/>one of:<br/>- string<br/>-&nbsp;object([Custom](/docs/references/schemas/Custom))<br/>-&nbsp;object([Executable](/docs/references/schemas/Executable)) | Optional. No description provided. |
24+
runOn | array of object | Optional. Contexts to run the test in. Overrides contexts defined at the config and spec levels. |
25+
fileTypes | array of string, object, object | Optional. Configuration for file types and their markup detection. |
2526
integrations | object | Optional. Options for connecting to external services. |
2627
integrations.openApi | array of unknown | Optional. No description provided. |
2728
telemetry | object | Optional. Options around sending telemetry for Doc Detective usage. | ``{"send":true}``

docs/references/schemas/context.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ A context in which to perform tests. If no contexts are specified but a context
88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
1010
contextId | string | Optional. Unique identifier for the context. |
11-
platforms | string | Optional. No description provided.<br/><br/>Accepted values: `linux`, `mac`, `windows` |
12-
platforms | array of string | Optional. No description provided. |
11+
platforms | string | Optional. Platforms to run tests on.<br/><br/>Accepted values: `linux`, `mac`, `windows` |
12+
platforms | array of string | Optional. Platforms to run tests on. |
1313
browsers | string | Optional. Name of the browser.<br/><br/>Accepted values: `chrome`, `firefox`, `safari`, `webkit` |
1414
browsers | object | Optional. Browser configuration. |
1515
browsers.name | string | Required. Name of the browser.<br/><br/>Accepted values: `chrome`, `firefox`, `safari`, `webkit` |
@@ -20,7 +20,7 @@ browsers.window.height | integer | Optional. Height of the browser window in pix
2020
browsers.viewport | object | Optional. Viewport dimensions. |
2121
browsers.viewport.width | integer | Optional. Width of the viewport in pixels. |
2222
browsers.viewport.height | integer | Optional. Height of the viewport in pixels. |
23-
browsers | array of <br/>one of:<br/>- string<br/>- object | Optional. No description provided. |
23+
browsers | array of string, object | Optional. Browsers to run tests on. |
2424
browsers[].name | string | Required. Name of the browser.<br/><br/>Accepted values: `chrome`, `firefox`, `safari`, `webkit` |
2525
browsers[].headless | boolean | Optional. If `true`, runs the browser in headless mode. | `true`
2626
browsers[].window | object | Optional. Browser dimensions. |

docs/references/schemas/find.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ Find an element based on display text or a selector, then optionally interact wi
77

88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
10+
find | string | Display text or selector of the element to find. |
1011
elementText | string | Optional. Display text of the element to find. If combined with `selector`, the element must match both the text and the selector. |
1112
selector | string | Optional. Selector of the element to find. If combined with `elementText`, the element must match both the text and the selector. |
1213
timeout | integer | Optional. Max duration in milliseconds to wait for the element to exist. | `5000`
1314
moveTo | boolean | Optional. Move to the element. If the element isn't visible, it's scrolled into view. | `true`
1415
click | object([click](/docs/references/schemas/click)) | Optional. Click or tap an element. |
15-
click | object | Optional. No description provided. |
16+
click | object | Optional. Click the element. |
1617
click.button | string | Optional. Kind of click to perform.<br/><br/>Accepted values: `left`, `right`, `middle` |
1718
type | unknown | Optional. Type keys after finding the element. Either a string or an object with a `keys` field as defined in [`type`](type). To type in the element, make the element active with the `click` parameter. |
1819

docs/references/schemas/goTo.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
Field | Type | Description | Default
99
:-- | :-- | :-- | :--
10-
url | string | Required. URL to navigate to. Can be a full URL or a path. If a path is provided and `origin` is specified, prepends `origin` to `url`. If a path is provided but `origin` isn't specified, attempts to navigate relative to the current URL, if any. |
10+
goTo | string | Navigate to an HTTP or HTTPS URL. Can be a full URL or a path. If a path is provided, navigates relative to the current URL, if any. |
11+
url | string | Required. URL to navigate to. Can be a full URL or a path. If a path is provided and `origin` is specified, prepends `origin` to `url`. If a path is provided but `origin` isn't specified, attempts to navigate relative to the current URL, if any.<br/><br/>Pattern: `(^(http://|https://|/).*|\$[A-Za-z0-9_]+)` |
1112
origin | string | Optional. Protocol and domain to navigate to. Prepended to `url`. |
1213

1314
## Examples

0 commit comments

Comments
 (0)