diff --git a/.scripts/buildSchemaReferences.js b/.scripts/buildSchemaReferences.js index c2ba6b95..57a791a6 100644 --- a/.scripts/buildSchemaReferences.js +++ b/.scripts/buildSchemaReferences.js @@ -11,25 +11,27 @@ main(); async function main() { const schemasToGenerate = [ - "checkLink_v2", - "config_v2", - "context_v2", - "find_v2", - "goTo_v2", - "httpRequest_v2", - "runShell_v2", - "runCode_v2", - "saveScreenshot_v2", - "setVariables_v2", - "startRecording_v2", - "stopRecording_v2", - "spec_v2", - "test_v2", - "typeKeys_v2", - "wait_v2", + "checkLink_v3", + "click_v3", + "config_v3", + "context_v3", + "find_v3", + "goTo_v3", + "httpRequest_v3", + "runShell_v3", + "runCode_v3", + "screenshot_v3", + "loadVariables_v3", + "record_v3", + "stopRecord_v3", + "spec_v3", + "step_v3", + "test_v3", + "type_v3", + "wait_v3", ]; for await (const key of schemasToGenerate) { - schema = schemas[key]; + let schema = schemas[key]; // Dereference schema schema = await parser.dereference(schema); // Format @@ -41,10 +43,42 @@ async function main() { "Field | Type | Description | Default", ":-- | :-- | :-- | :--", ]; - const keys = Object.keys(schema.properties); - for (const key in keys) { - let field = keys[key]; - let fieldDetails = parseField(schema, field); + + // Extract all unique top-level property keys, handling direct properties, anyOf/oneOf, and allOf structures + const propertyKeys = new Set(); + + // Handle direct properties if they exist + if (schema.properties) { + Object.keys(schema.properties).forEach(key => propertyKeys.add(key)); + } + + // Handle polymorphic schemas with anyOf or oneOf + if (schema.anyOf || schema.oneOf) { + const variants = schema.anyOf || schema.oneOf; + + for (const variant of variants) { + // Check for properties directly within the variant + if (variant.properties) { + Object.keys(variant.properties).forEach(key => propertyKeys.add(key)); + } + // Check for properties within an allOf structure inside the variant + if (variant.allOf) { + for (const allOfItem of variant.allOf) { + if (allOfItem.properties) { + Object.keys(allOfItem.properties).forEach(key => propertyKeys.add(key)); + } + } + } + } + } + + // Get the unique keys as an array + const keys = Array.from(propertyKeys); + + // Process each top-level property + for (const field of keys) { // Changed loop variable name for clarity + // let field = keys[key]; // Old line + let fieldDetails = parseField(schema, field); // Pass only the top-level field name fields = fields.concat(fieldDetails); } fields.push(""); @@ -95,9 +129,46 @@ function parseField(schema, fieldName, fieldNameBase) { } else { name = fieldName; } - let property = schema.properties[fieldName]; + + // Find the property definition, handling direct properties, anyOf/oneOf, and allOf structures + let property; + + // Check direct properties first + if (schema.properties && schema.properties[fieldName]) { + property = schema.properties[fieldName]; + } + // If not found, check in anyOf/oneOf variants + else if (schema.anyOf || schema.oneOf) { + const variants = schema.anyOf || schema.oneOf; + + // Look for the property in each variant + for (const variant of variants) { + // Check direct properties within the variant + if (variant.properties && variant.properties[fieldName]) { + property = variant.properties[fieldName]; + break; // Found it + } + // Check within an allOf structure inside the variant + if (variant.allOf) { + for (const allOfItem of variant.allOf) { + if (allOfItem.properties && allOfItem.properties[fieldName]) { + property = allOfItem.properties[fieldName]; + break; // Found it + } + } + } + if (property) break; // Found it in the inner loop + } + } + + // If property still not found, handle the error + if (!property) { + console.warn(`Warning: Property '${fieldName}' not found in schema '${schema.title || 'unknown'}' or its variants`); + return [`${name} | unknown | Property definition not found | `]; + } + let typeDetails = getTypes(property); - let description = property.description; + let description = property.description || "No description provided."; // Get required if (schema.required && schema.required.includes(fieldName)) { description = "Required. " + description; @@ -148,19 +219,31 @@ function parseField(schema, fieldName, fieldNameBase) { // Parse child objects // Check if has child properties if (typeDetails.type === "object") { - let childProperties; + let childProperties = []; if (property.properties) childProperties = [property.properties]; if (property.anyOf || property.oneOf) { let xOfArray = property.anyOf || property.oneOf; - childProperties = xOfArray.filter((item) => item.property); + // Improved filtering for polymorphic objects + let polymorphicObjects = xOfArray.filter((item) => + item.properties || (item.type === "object" && item.title) + ); + + // Add each variant's properties to childProperties + for (const obj of polymorphicObjects) { + if (obj.properties) childProperties.push(obj.properties); + } } + + // Process all child properties for (const prop in childProperties) { property.properties = childProperties[prop]; - const keys = Object.keys(property.properties); - for (const key in keys) { - let field = keys[key]; - let fieldDetails = parseField(property, field, name); - for (const detail in fieldDetails) details.push(fieldDetails[detail]); + if (property.properties) { + const keys = Object.keys(property.properties); + for (const key in keys) { + let field = keys[key]; + let fieldDetails = parseField(property, field, name); + for (const detail in fieldDetails) details.push(fieldDetails[detail]); + } } } } @@ -169,7 +252,7 @@ function parseField(schema, fieldName, fieldNameBase) { let itemsArray = getItems(property, "object"); for (const index in itemsArray) { const item = itemsArray[index]; - if (item.type === "object" && !item.title) { + if (item.type === "object" && !item.title && item.properties) { // console.log(item); const keys = Object.keys(item.properties); for (const key in keys) { @@ -185,47 +268,110 @@ function parseField(schema, fieldName, fieldNameBase) { } function getItems(property, typeFilter) { - let items; - if (property.items && (property.items.anyOf || property.items.oneOf)) { - items = property.items.anyOf || property.items.oneOf; - } else if (property.items) { - items = [property.items]; + let items = []; + + // Handle direct items property + if (property.items) { + if (property.items.anyOf || property.items.oneOf) { + // Array items with polymorphic types + items = property.items.anyOf || property.items.oneOf; + } else { + // Single type array items + items = [property.items]; + } } - if ( - (property.anyOf && property.anyOf.items) || - (property.oneOf && property.oneOf.items) - ) { - items = property.anyOf.items || property.oneOf.items; + + // Handle items in anyOf/oneOf variants + if (property.anyOf || property.oneOf) { + const variants = property.anyOf || property.oneOf; + + // Collect items from all variants that have them + for (const variant of variants) { + if (variant.items) { + if (variant.items.anyOf || variant.items.oneOf) { + // Add all polymorphic items + const variantItems = variant.items.anyOf || variant.items.oneOf; + items = items.concat(variantItems); + } else { + // Add single item + items.push(variant.items); + } + } + } } - if (typeFilter) items = items.filter((item) => item.type === typeFilter); + + // Apply type filter if specified + if (typeFilter && items.length > 0) { + items = items.filter((item) => item.type === typeFilter); + } + return items; } function getTypes(property) { - // TODO: Include supported subtypes within array let type; let types = []; + // Get types if (!property.type && (property.anyOf || property.oneOf)) { - xOfArray = property.anyOf || property.oneOf; - types = xOfArray.filter((xOf) => xOf.type); + // Handle polymorphic properties with anyOf or oneOf + const xOfArray = property.anyOf || property.oneOf; + const typesArray = xOfArray.filter((xOf) => xOf.type || xOf.title); + + // Extract type information from each variant + types = typesArray.map(item => { + if (item.type) return item.type; + if (item.title) return "object"; + return "unknown"; + }); + if (types.length > 1) { + // Format for multiple type options type = "One of"; - for (const item of types) { - type = type + `
- ${item.type}`; - if (item.type === "array") { - subTypes = getArraySubTypes(item, 1); + for (const index in typesArray) { + const item = typesArray[index]; + const itemType = types[index]; + + // Basic type display + type = type + `
- ${itemType}`; + + // Add title for referenced objects + if (item.title) { + type = type + `([${item.title}](/docs/references/schemas/${item.title}))`; + } + + // Handle array subtypes + if (itemType === "array") { + const subTypes = getArraySubTypes(item, 1); type = type + subTypes; } } - } else { + } else if (types.length === 1) { + // Single type from oneOf/anyOf type = types[0]; + + // Add title for referenced objects + const item = typesArray[0]; + if (item.title) { + type = type + `([${item.title}](/docs/references/schemas/${item.title}))`; + } + + // Handle array subtypes + if (type === "array") { + const subTypes = getArraySubTypes(item, 0); + type = type + subTypes; + } + } else { + type = "unknown"; } } else if (property.type) { + // Direct type definition type = property.type; types = [type]; + + // Handle array subtypes if (type === "array") { - subTypes = getArraySubTypes(property, 0); + const subTypes = getArraySubTypes(property, 0); type = type + subTypes; } } @@ -236,23 +382,79 @@ function getTypes(property) { function getArraySubTypes(property, depth) { let subTypes = " of "; let itemsArray = getItems(property); + + // If no items found + if (!itemsArray || itemsArray.length === 0) { + return " of any"; + } + + // For multiple item types (polymorphic array) if (itemsArray.length > 1) { let spaces = ""; if (Number(depth) === 1) spaces = "  "; + + subTypes += "
" + spaces + "one of:"; + for (const index in itemsArray) { - item = itemsArray[index]; + const item = itemsArray[index]; + // Handle referenced schema objects if (item.type === "object" && item.title) { subTypes = `${subTypes}
${spaces}- ${item.type}([${item.title}](/docs/references/schemas/${item.title}))`; - } else { - subTypes = `${subTypes}
${spaces}- ${item.type}s`; + } + // Handle nested oneOf/anyOf in array items + else if (!item.type && (item.oneOf || item.anyOf)) { + const nestedTypes = (item.oneOf || item.anyOf) + .map(variant => { + if (variant.type === "object" && variant.title) { + return `${variant.type}([${variant.title}](/docs/references/schemas/${variant.title}))`; + } else if (variant.type) { + return variant.type; + } else { + return "unknown"; + } + }) + .join(", "); + subTypes = `${subTypes}
${spaces}- one of: ${nestedTypes}`; + } + // Handle regular types + else if (item.type) { + subTypes = `${subTypes}
${spaces}- ${item.type}`; + } + // Handle unknown types + else { + subTypes = `${subTypes}
${spaces}- unknown`; } } - } else { - item = itemsArray[0]; + } + // For single item type + else { + const item = itemsArray[0]; + // Handle referenced schema objects if (item.type === "object" && item.title) { subTypes = `${subTypes}${item.type}([${item.title}](/docs/references/schemas/${item.title}))`; - } else { - subTypes = subTypes + item.type + "s"; + } + // Handle nested oneOf/anyOf in array items + else if (!item.type && (item.oneOf || item.anyOf)) { + subTypes += "one of: "; + const variants = item.oneOf || item.anyOf; + const variantTypes = variants.map(variant => { + if (variant.type === "object" && variant.title) { + return `${variant.type}([${variant.title}](/docs/references/schemas/${variant.title}))`; + } else if (variant.type) { + return variant.type; + } else { + return "unknown"; + } + }).join(", "); + subTypes += variantTypes; + } + // Handle regular types + else if (item.type) { + subTypes += item.type; + } + // Handle unknown types + else { + subTypes += "unknown"; } } @@ -263,10 +465,30 @@ function getArraySubTypes(property, depth) { function getSchemaFormat(schema, depth) { let format = ["{"]; if (!depth) depth = 1; - // console.log(Object.keys(schema.properties)); - // exit() - let properties = schema.properties; - for (const [key, value] of Object.entries(properties)) { + + // Handle properties from multiple sources + let allProperties = {}; + + // Handle direct properties if they exist + if (schema.properties) { + allProperties = { ...schema.properties }; + } + + // Handle polymorphic schemas with anyOf or oneOf + if (schema.anyOf || schema.oneOf) { + const variants = schema.anyOf || schema.oneOf; + + // Extract properties from each variant that has them + for (const variant of variants) { + if (variant.properties) { + // Merge properties from this variant + allProperties = { ...allProperties, ...variant.properties }; + } + } + } + + // Process all properties + for (const [key, value] of Object.entries(allProperties)) { console.log({ key, value }); const typeDetails = getTypes(value); console.log(typeDetails.types); @@ -296,34 +518,117 @@ function getSchemaFormat(schema, depth) { return format; } -// TODO: Figure out formatting for oneOfs with arrays/objects function getFormatDescription(name, property, depth) { let description = []; if (!depth) depth = 1; - if (property.type === "object" && property.title) { - description.push(`${" ".repeat(depth)}"${name}": {`); - depth = depth + 1; + const indent = " ".repeat(depth); + + // Handle polymorphic properties (oneOf/anyOf) + if (!property.type && (property.oneOf || property.anyOf)) { + const variants = property.oneOf || property.anyOf; + description.push(`${indent}"${name}": /* One of the following: */`); + + // Add variants as comments + for (const variant of variants) { + if (variant.type === "object" && variant.title) { + description.push(`${indent}/* - object: ${variant.title} schema */`); + } else if (variant.type === "array") { + const itemTypes = getArrayItemType(variant); + description.push(`${indent}/* - array of ${itemTypes} */`); + } else if (variant.type) { + description.push(`${indent}/* - ${variant.type} */`); + } + } + + // Use the first variant for the example + const firstVariant = variants[0]; + if (firstVariant.type === "object" && firstVariant.title) { + description.push(`${indent}{`); + description.push(`${indent} /* See ${firstVariant.title} schema */`); + description.push(`${indent}},`); + } else if (firstVariant.type === "object") { + description.push(`${indent}{`); + description.push(`${indent} /* Object with custom properties */`); + description.push(`${indent}},`); + } else if (firstVariant.type === "array") { + const itemTypes = getArrayItemType(firstVariant); + description.push(`${indent}[ /* Array of ${itemTypes} */ ],`); + } else if (firstVariant.type === "string" && firstVariant.const) { + description.push(`${indent}"${firstVariant.const}",`); + } else if (firstVariant.type) { + description.push(`${indent}${firstVariant.type},`); + } else { + description.push(`${indent}/* Complex polymorphic type */,`); + } + } + // Handle object with title reference + else if (property.type === "object" && property.title) { + description.push(`${indent}"${name}": {`); description.push( - `${" ".repeat(depth)}object([${property.title}](/docs/references/schemas/${ - property.title - }))` + `${indent} /* See ${property.title} schema */` ); - depth = depth - 1; - description.push(`${" ".repeat(depth)}},`); - } else if (property.type === "object" && !property.title) { - description.push(`${" ".repeat(depth)}"${name}": {`); - depth = depth + 1; - description.push(`${" ".repeat(depth)}object`); - depth = depth - 1; - description.push(`${" ".repeat(depth)}},`); - } else if (property.type === "array") { - description.push(`${" ".repeat(depth)}"${name}": [ ${property.type} ],`); - } else if (property.type === "string" && property.const) { - description.push(`${" ".repeat(depth)}"${name}": "${property.const}",`); + description.push(`${indent}},`); + } + // Handle regular object + else if (property.type === "object" && !property.title) { + description.push(`${indent}"${name}": {`); + description.push(`${indent} /* Object with custom properties */`); + description.push(`${indent}},`); + } + // Handle array + else if (property.type === "array") { + const itemTypes = getArrayItemType(property); + description.push(`${indent}"${name}": [ /* Array of ${itemTypes} */ ],`); + } + // Handle string constants + else if (property.type === "string" && property.const) { + description.push(`${indent}"${name}": "${property.const}",`); + } + // Handle string enums + else if (property.type === "string" && property.enum) { + const enumValues = property.enum.map(val => `"${val}"`).join(" | "); + description.push(`${indent}"${name}": /* One of: ${enumValues} */,`); + } + // Handle other types + else if (property.type) { + description.push(`${indent}"${name}": ${property.type},`); + } + // Handle unknown types + else { + description.push(`${indent}"${name}": /* Unknown type */,`); + } + + return description.join("\n"); +} + +// Helper function to get array item type description +function getArrayItemType(property) { + const items = getItems(property); + if (!items || items.length === 0) { + return "any"; + } + + if (items.length === 1) { + const item = items[0]; + if (item.type === "object" && item.title) { + return `${item.title} objects`; + } else if (item.type) { + return `${item.type}s`; + } else if (item.oneOf || item.anyOf) { + const variants = item.oneOf || item.anyOf; + const types = variants.map(v => { + if (v.type === "object" && v.title) return v.title; + return v.type || "unknown"; + }).join(" | "); + return `[${types}]`; + } } else { - description.push(`${" ".repeat(depth)}"${name}": ${property.type},`); + const types = items.map(item => { + if (item.type === "object" && item.title) return item.title; + return item.type || "unknown"; + }).join(" | "); + return `[${types}]`; } - console.log(description); - description = description.join("\n"); - return description; + + return "unknown"; } diff --git a/docs/get-started/actions/checkLink.md b/docs/get-started/actions/checkLink.md index 9d70323d..51c7c25c 100644 --- a/docs/get-started/actions/checkLink.md +++ b/docs/get-started/actions/checkLink.md @@ -11,10 +11,11 @@ description: Check if a URL returns an acceptable status code from a GET request The `checkLink` action checks if a URL returns an acceptable status code from a GET request. This action is useful for verifying that a hyperlink or image URL is valid. -You can also specify +You can specify the target URL directly as a string, or use an object for more options: -- an `origin` to navigate to a URL relative to a specific path. -- `statusCodes` to set acceptable HTTP status codes. +- `url`: (Required in object format) The URL to check. Can be a full URL or a path. If a path is provided, an `origin` must be specified either in the step or in the configuration file. +- `origin`: (Optional) Protocol and domain prepended to `url` when `url` is a path. If omitted and `url` is a path, the global `origin` from the configuration file is used. +- `statusCodes`: (Optional) A single integer or an array of integers representing acceptable HTTP status codes. If omitted, defaults to `[200, 301, 302, 307, 308]`. > For comprehensive options, see the [`checkLink`](/docs/references/schemas/checkLink) reference. @@ -22,7 +23,7 @@ You can also specify Here are a few ways you might use the `checkLink` action: -### Check if a link is valid +### Check if a link is valid (string shorthand) ```json { @@ -31,8 +32,46 @@ Here are a few ways you might use the `checkLink` action: "steps": [ { "description": "Check if Google is up.", - "action": "checkLink", - "url": "https://www.google.com" + "checkLink": "https://www.google.com" + } + ] + } + ] +} +``` + +### Check if a link is valid (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Check if Google is up.", + "checkLink": { + "url": "https://www.google.com" + } + } + ] + } + ] +} +``` + +### Check for a specific status code response + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Check if Google is up, expecting only 200.", + "checkLink": { + "url": "https://www.google.com", + "statusCodes": 200 + } } ] } @@ -49,9 +88,10 @@ Here are a few ways you might use the `checkLink` action: "steps": [ { "description": "Check if Google is up with extra status codes.", - "action": "checkLink", - "url": "https://www.google.com", - "statusCodes": [200, 201, 202] + "checkLink": { + "url": "https://www.google.com", + "statusCodes": [200, 201, 202] + } } ] } @@ -67,10 +107,32 @@ Here are a few ways you might use the `checkLink` action: { "steps": [ { - "description": "Check if Google is up with an origin.", - "action": "checkLink", - "url": "/search", - "origin": "https://www.google.com" + "description": "Check if Google search path is valid using a specific origin.", + "checkLink": { + "url": "/search", + "origin": "https://www.google.com" + } + } + ] + } + ] +} +``` + +### Check a relative link using global origin + +Assuming a global `origin` of `https://www.google.com` is set in the configuration: + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Check if Google search path is valid using global origin.", + "checkLink": { + "url": "/search" + } } ] } @@ -97,9 +159,10 @@ Consider the following test configuration, which checks the validity of `https:/ "steps": [ { "description": "Check site with a self-signed certificate", - "action": "checkLink", - "url": "https://self-signed.badssl.com/", - "statusCodes": [200, 201, 202, 301] + "checkLink": { + "url": "https://self-signed.badssl.com/", + "statusCodes": [200, 201, 202, 301] + } } ] } @@ -134,7 +197,7 @@ To fix this issue, follow these steps: NODE_TLS_REJECT_UNAUTHORIZED=0 ``` -2. Modify your test configuration to include a `setVariables` action: +2. Modify your test configuration to include a `loadVariables` step (Note: `setVariables` is deprecated, use `loadVariables`): ```json title="bad-certificate.json" {5-8} { @@ -142,14 +205,14 @@ To fix this issue, follow these steps: { "steps": [ { - "action": "setVariables", - "path": "ignore-certificate-problems.env" + "loadVariables": "ignore-certificate-problems.env" }, { "description": "Check self-signed.badssl.com", - "action": "checkLink", - "url": "https://self-signed.badssl.com/", - "statusCodes": [200, 201, 202, 301] + "checkLink": { + "url": "https://self-signed.badssl.com/", + "statusCodes": [200, 201, 202, 301] + } } ] } diff --git a/docs/get-started/actions/click.md b/docs/get-started/actions/click.md new file mode 100644 index 00000000..e46077d6 --- /dev/null +++ b/docs/get-started/actions/click.md @@ -0,0 +1,135 @@ +--- +title: click +layout: default +nav_order: 2 +parent: Actions +grand_parent: Tests +description: Click or tap an element on the page. +--- + +# click + +The `click` action allows you to click or tap an element on the page. You can specify which mouse button to use and target elements using text or selectors. + +The `click` action works in several ways: + +- **String Shorthand:** The display text or selector of the element to find and click. +- **Object Format:** Use an object with detailed properties for more control over the click action. + +If you need to find an element before clicking it, consider using the [`find`](/docs/get-started/actions/find) action, which lets you locate elements and optionally click them in a single step. + +**Note:** If you use the `click` action without specifying a target (using element text or selector), it will click the active element or the element at the current cursor position. + +## Properties + +When using the object format, you can specify: + +- `button`: (Optional) Kind of click to perform. Can be `"left"`, `"right"`, or `"middle"`. Default is `"left"`. +- `selector`: (Optional) CSS or XPath selector of the element to click. If combined with `elementText`, the element must match both the text and the selector. +- `elementText`: (Optional) Display text of the element to click. If combined with `selector`, the element must match both the text and the selector. + +> For comprehensive options, see the full [`click`](/docs/references/schemas/click) reference. + +## Examples + +Here are a few ways you might use the `click` action: + +### Click by element text (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Click on an element with text 'Submit'", + "click": "Submit" + } + ] + } + ] +} +``` + +### Click by selector (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Click on an element matching the CSS selector", + "click": "#submit-button" + } + ] + } + ] +} +``` + +### Click an element by text (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Left-click on a button with specific text", + "click": { + "elementText": "Submit", + "button": "left" + } + } + ] + } + ] +} +``` + +### Click an element by selector (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Middle-click on an element with a specific selector", + "click": { + "selector": "#open-in-new-tab", + "button": "middle" + } + } + ] + } + ] +} +``` + +### Click an element by both selector and text + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Click on a specific element matching both selector and text", + "click": { + "selector": ".btn", + "elementText": "Download", + "button": "left" + } + } + ] + } + ] +} +``` + +## Related Actions + +- [`find`](/docs/get-started/actions/find): Find an element and optionally interact with it +- [`type`](/docs/get-started/actions/type): Type text or special keys diff --git a/docs/get-started/actions/find.md b/docs/get-started/actions/find.md index 354b01bb..1b02d5cf 100644 --- a/docs/get-started/actions/find.md +++ b/docs/get-started/actions/find.md @@ -9,6 +9,162 @@ description: Locate and interact with an element on the page. # find -The `find` action locates an element in the current interface and interacts with it, such as validating its text content, clicking it, or typing into it. +The `find` action locates an element in the current interface based on its display text or a CSS/XPath selector. After finding the element, you can optionally interact with it, such as clicking it or typing into it. + +If multiple elements match the specified criteria (`elementText` and/or `selector`), the step operates on the first matched element. Ensure your criteria uniquely identify the target element. + +You can specify the target element directly using a string (for simple text or selector lookup) or use an object for more detailed options and interactions: + +- **String Shorthand:** Provide the display text or CSS/XPath selector directly as the value for the `find` key. If elements are found by both text and selector, the element found by matching text is used. + - Example: `"find": "Login"` or `"find": "#username"`. +- **Object Format:** Use an object with the following properties: + - `elementText`: (Optional) The display text of the element to find. If combined with `selector`, the element must match both. + - `selector`: (Optional) The CSS or XPath selector of the element to find. If combined with `elementText`, the element must match both. *At least one of `elementText` or `selector` is required.* + - `timeout`: (Optional) Maximum duration in milliseconds to wait for the element to exist (default: 5000). + - `moveTo`: (Optional) Move the cursor to the element. If the element isn't visible, it's scrolled into view (default: `true`). + - `click`: (Optional) Click the element after finding it. Can be `true` (for a default left click), `"left"`, `"right"`, `"middle"`, or an object like `{ "button": "right" }`. + - `type`: (Optional) Type keys after finding the element. Requires the element to be made active first (e.g., by using `click: true`). Accepts a string or an object like `{ "keys": "my text", "inputDelay": 100 }`. See [`type`](type) for details. + +**Setting Variables:** To capture a found element's attributes into variables for later steps, use the step-level `variables` object. You can assign values based on the element using expressions like `$$element.text`. > For comprehensive options, see the [`find`](/docs/references/schemas/find) reference. + +## Examples + +Here are a few ways you might use the `find` action: + +### Find an element by its text (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find the login button by its text.", + "find": "Login" + } + ] + } + ] +} +``` + +### Find an element by its selector (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find the username field by its ID (CSS selector).", + "find": "#username" + } + ] + } + ] +} +``` + +### Find an element by selector and click it (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find the submit button by selector and click it.", + "find": { + "selector": "button[type='submit']", + "click": true + } + } + ] + } + ] +} +``` + +### Find an element by selector, click it, and type into it (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find the search input by selector, click, and type.", + "find": { + "selector": "#input", + "click": true, + "type": "find action" + } + } + ] + } + ] +} +``` + +### Find an element combining selector and text with a timeout + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find a specific link within a nav bar, waiting up to 10 seconds.", + "find": { + "selector": "nav > ul > li > a", + "elementText": "Downloads", + "timeout": 10000 + } + } + ] + } + ] +} +``` + +### Find an element and right-click it + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find an image by selector and right-click it.", + "find": { + "selector": "img.product-image", + "click": "right" + } + } + ] + } + ] +} +``` + +### Find an element and middle-click it + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Find a button by text and middle-click it.", + "find": { + "elementText": "Open New Tab", + "click": "middle" + } + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/goTo.md b/docs/get-started/actions/goTo.md index 0a38bb47..edcf2294 100644 --- a/docs/get-started/actions/goTo.md +++ b/docs/get-started/actions/goTo.md @@ -9,14 +9,23 @@ description: Navigate to a specified URL. # goTo -The `goTo` action navigates to a URL. This action is useful for starting a test at a specific page. +The `goTo` action navigates the browser to a specified URL. This is typically used to start a test at a specific page or navigate between pages during a test. -You can also specify an `origin` to navigate to a URL relative to a specific path. +You can specify the target URL directly as a string, or use an object for more options: + +- **String Shorthand:** Provide the full URL, a path (starting with `/`), or a variable reference directly as the value for the `goTo` key. If a path is provided, it navigates relative to the current URL or the global `origin` if set in the configuration. +- **Object Format:** Use an object with the following properties: + - `url`: (Required) The URL to navigate to. Can be a full URL, a path, or a variable reference. + - `origin`: (Optional) Protocol and domain prepended to `url` when `url` is a path. Overrides the global `origin` if set. > For comprehensive options, see the [`goTo`](/docs/references/schemas/goTo) reference. ## Examples +Here are a few ways you might use the `goTo` action: + +### Navigate to a full URL (string shorthand) + ```json { "tests": [ @@ -24,8 +33,7 @@ You can also specify an `origin` to navigate to a URL relative to a specific pat "steps": [ { "description": "Navigate to example.com.", - "action": "goTo", - "url": "https://example.com" + "goTo": "https://example.com" } ] } @@ -33,16 +41,57 @@ You can also specify an `origin` to navigate to a URL relative to a specific pat } ``` +### Navigate to a relative path (string shorthand) + +Assumes the browser is currently at `https://example.com` or a global `origin` is set. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Navigate to the /about path.", + "goTo": "/about" + } + ] + } + ] +} +``` + +### Navigate using an object with a full URL + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Navigate to example.com using object format.", + "goTo": { + "url": "https://example.com" + } + } + ] + } + ] +} +``` + +### Navigate using an object with a relative path and specific origin + ```json { "tests": [ { "steps": [ { - "description": "Navigate to with an origin.", - "action": "goTo", - "url": "/search", - "origin": "https://www.google.com" + "description": "Navigate to Google search using a specific origin.", + "goTo": { + "url": "/search", + "origin": "https://www.google.com" + } } ] } diff --git a/docs/get-started/actions/httpRequest.md b/docs/get-started/actions/httpRequest.md index b432b9b2..64a8a48a 100644 --- a/docs/get-started/actions/httpRequest.md +++ b/docs/get-started/actions/httpRequest.md @@ -9,6 +9,271 @@ description: Perform a generic HTTP request, for example to an API. # httpRequest -The `httpRequest` action makes arbitrary HTTP calls, allowing you to call and validate APIs and use web services. +The `httpRequest` action makes arbitrary HTTP calls, allowing you to interact with and validate APIs or other web services directly within your tests. + +You can specify a simple GET request using a string shorthand or use an object for more complex requests and validation: + +- **String Shorthand:** Provide the full URL directly as the value for the `httpRequest` key. This performs a simple GET request to that URL. +- **Object Format:** Use an object with the following properties: + - `url`: (Required unless using `openApi`) The target URL for the request. + - `method`: (Optional) The HTTP method (e.g., `GET`, `POST`, `PUT`, `DELETE`). Defaults to `GET`. + - `timeout`: (Optional) Maximum duration in milliseconds to wait for the request to complete. + - `request`: (Optional) An object defining the request details: + - `headers`: (Optional) Key-value pairs for request headers. + - `parameters`: (Optional) Key-value pairs for query string parameters. + - `body`: (Optional) The request body. Can be a string or JSON object. + - `response`: (Optional) An object defining expected response validation: + - `headers`: (Optional) Key-value pairs for expected response headers. Values must be strings. + - `body`: (Optional) Expected response body. Can be a string or JSON object. + - `statusCodes`: (Optional) An array of acceptable HTTP status codes. If the response code is not in this list, the step fails (default: `[200]`). + - `openApi`: (Optional) Define the request based on an OpenAPI definition. Can be a string (operation ID) or an object: + - `name`: (Optional) Name of the registered OpenAPI definition (if multiple are loaded). + - `descriptionPath`: (Optional) Path or URL to the OpenAPI description file. + - `operationId`: (Required) The ID of the operation to use. + - `useExample`: (Optional) Use example data from the OpenAPI spec (`request`, `response`, or `both`). + - `exampleKey`: (Optional) Key of the specific example to use if multiple exist. + *Note: Properties like `request.headers`, `request.parameters`, `request.body` can override values from the OpenAPI definition or example.* + - *Output Saving:* You can also save the response body using `path`, `directory`, `maxVariation`, and `overwrite` properties. See the [`httpRequest`](/docs/references/schemas/httpRequest) reference for details. + +**Setting Variables:** To capture parts of the response for later steps, use the step-level `variables` object. You can assign values based on the response using expressions like `$$response.body`, `$$response.headers`, `$$response.status`, etc. You can use dot notation for nested JSON fields (e.g., `$$response.body.user.id`). > For comprehensive options, see the [`httpRequest`](/docs/references/schemas/httpRequest) reference. + +## Examples + +Here are a few ways you might use the `httpRequest` action: + +### Simple GET request (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Perform a simple GET request.", + "httpRequest": "https://reqres.in/api/users?page=2" + } + ] + } + ] +} +``` + +### Simple GET request (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Perform a simple GET request using object format.", + "httpRequest": { + "url": "https://reqres.in/api/users?page=2" + } + } + ] + } + ] +} +``` + +### POST request with JSON body + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Create a user via POST request.", + "httpRequest": { + "url": "https://reqres.in/api/users", + "method": "POST", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + } + } + } + ] + } + ] +} +``` + +### PUT request with headers and query parameters + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Update a user via PUT request.", + "httpRequest": { + "url": "https://reqres.in/api/users/2", + "method": "PUT", + "request": { + "headers": { + "Content-Type": "application/json" + }, + "parameters": { + "source": "test" + }, + "body": { + "name": "morpheus", + "job": "zion resident" + } + } + } + } + ] + } + ] +} +``` + +### Validate response status code and body + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Create user and validate response.", + "httpRequest": { + "url": "https://reqres.in/api/users", + "method": "POST", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "response": { + "body": { + "name": "morpheus", + "job": "leader", + } + }, + "statusCodes": [201] // Expect HTTP 201 Created + } + } + ] + } + ] +} +``` + +### Use OpenAPI definition (by operation ID) + +Assumes an OpenAPI definition with operation ID `getUserById` is loaded. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Get user by ID using OpenAPI definition.", + "httpRequest": { + "openApi": "getUserById", + "request": { + "parameters": { // Provide required path parameter + "id": 2 + } + } + } + } + ] + } + ] +} +``` + +### Use OpenAPI definition (detailed object) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Get user by ID using specific OpenAPI file.", + "httpRequest": { + "openApi": { + "descriptionPath": "./reqres.openapi.json", + "operationId": "getUserById" + }, + "request": { + "parameters": { + "id": 3 + } + } + } + } + ] + } + ] +} +``` + +### Set a variable from response body + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Create user and capture the ID.", + "httpRequest": { + "url": "https://reqres.in/api/users", + "method": "POST", + "request": { + "body": {"name": "neo", "job": "the one"} + }, + "statusCodes": [201] + }, + "variables": { + "USER_ID": "$$response.body.id" + } + }, + { + "description": "Use the captured ID in the next request.", + "httpRequest": { + "url": "https://reqres.in/api/users/$USER_ID", // Use variable in URL + "method": "GET" + } + } + ] + } + ] +} +``` + +### Save response body to a file + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Get user data and save response to file.", + "httpRequest": { + "url": "https://reqres.in/api/users/2", + "method": "GET", + "path": "user_2_response.json", // File name + "directory": "./output/api_responses" // Output directory + } + } + ] + } + ] +} diff --git a/docs/get-started/actions/loadVariables.md b/docs/get-started/actions/loadVariables.md new file mode 100644 index 00000000..b3c96339 --- /dev/null +++ b/docs/get-started/actions/loadVariables.md @@ -0,0 +1,54 @@ +--- +title: loadVariables +layout: default +nav_order: 1 +parent: Actions +grand_parent: Tests +description: Load environment variables from a .env file. +--- + +# loadVariables + +The `loadVariables` action loads environment variables from a specified `.env` file. This action is useful for accessing sensitive information, such as API keys or other credentials, without hardcoding them into your tests. Variables loaded this way are available for use in subsequent steps within the same test. + +> You can also load globally applicable variables using the `loadVariables` property in the configuration file. This makes them available across all tests. +> +> For comprehensive options, see the [`loadVariables`](/docs/references/schemas/loadVariables) reference. + +## Example + +Assuming a file named `secrets.env` exists in the same directory as the test file or at the specified path: + +```env title="secrets.env" +API_KEY=your_secret_api_key +BASE_URL=https://api.example.com +``` + +You can load these variables using the `loadVariables` action: + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Load environment variables from secrets.env.", + "loadVariables": "./secrets.env" + }, + { + "description": "Use the loaded API key in an HTTP request.", + "httpRequest": { + "url": "$BASE_URL/data", // Use loaded BASE_URL + "method": "GET", + "request": { + "headers": { + "Authorization": "Bearer $API_KEY" // Use loaded API_KEY + } + } + } + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/record.md b/docs/get-started/actions/record.md new file mode 100644 index 00000000..752a0898 --- /dev/null +++ b/docs/get-started/actions/record.md @@ -0,0 +1,115 @@ +--- +title: record +layout: default +nav_order: 1 +parent: Actions +grand_parent: Tests +description: Start capturing a video of test execution. +--- + +# record + +The `record` action starts recording the browser viewport as a video file (MP4, WebM, or GIF). This is useful for debugging failed tests or creating multimedia documentation. Recording continues until a `stopRecord` step is encountered. + +*Note: Recording is currently only supported in visible Chrome browsers.* + +You can specify the recording action in several ways: + +- **Boolean Shorthand:** Set `record: true` to start recording with default settings (usually saved as `record_.mp4` in the output directory). +- **String Shorthand:** Provide a file path directly as the value for the `record` key (e.g., `record: "test_session.webm"`). This starts recording to the specified path. Supported extensions are `.mp4`, `.webm`, and `.gif`. +- **Object Format:** Use an object for more control over the output file: + - `path`: (Optional) The file path where the recording will be saved. Can include directories. If omitted, a default path is generated. + - `directory`: (Optional) The directory where the recording will be saved. If `path` includes a directory, this is ignored. Defaults to the configured output directory. + - `overwrite`: (Optional) Set to `true` to overwrite an existing file at the target path. Defaults to `false`. + +**Stopping the Recording:** You *must* include a `stopRecord` step later in your test to finalize and save the video file. + +> For comprehensive options, see the [`record`](/docs/references/schemas/record) and [`stopRecord`](/docs/references/schemas/stopRecord) references. + +## Examples + +Here are a few ways you might use the `record` and `stopRecord` actions: + +### Simple recording (boolean shorthand) + +Starts recording to a default path like `output/record_1745032062266.mp4` and stops later. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Start recording.", + "record": true + }, + { + "description": "Perform some actions...", + "goTo": "https://example.com" + }, + { + "description": "Stop recording.", + "stopRecord": true + } + ] + } + ] +} +``` + +### Recording to a specific path (string shorthand) + +Records to `./output/media/login_flow.webm`. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Start recording to a specific WebM file.", + "record": "./output/media/login_flow.webm" + }, + { + "description": "...", + "find": "Login" + }, + { + "description": "Stop recording.", + "stopRecord": true + } + ] + } + ] +} +``` + +### Recording with object format (overwrite enabled) + +Records to `test_video.gif` in the `output/gifs` directory, overwriting if it exists. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Start recording a GIF, overwriting if necessary.", + "record": { + "path": "test_video.gif", + "directory": "./output/gifs", + "overwrite": true + } + }, + { + "description": "...", + "wait": 2000 + }, + { + "description": "Stop recording.", + "stopRecord": true + } + ] + } + ] +} diff --git a/docs/get-started/actions/runCode.md b/docs/get-started/actions/runCode.md new file mode 100644 index 00000000..3236c3f3 --- /dev/null +++ b/docs/get-started/actions/runCode.md @@ -0,0 +1,138 @@ +--- +title: runCode +layout: default +nav_order: 1 # Adjust nav_order as needed +parent: Actions +grand_parent: Tests +description: Assemble and run code snippets in various languages. +--- + +# runCode + +The `runCode` action executes a code snippet in a specified language (like JavaScript, Python, Bash, etc.) on the local machine and evaluates the results. This allows embedding executable code directly within your tests. + +`runCode` executes the code using the appropriate interpreter available on the machine. + +You must specify the `runCode` action using an object format with the following properties: + +- **Object Format:** + - `language`: (Required) The programming language of the code snippet (`javascript`, `python`, `bash`). + + > Note: `bash` isn't currently supported on Windows. + + - `code`: (Required) The code snippet to execute as a string. + - `workingDirectory`: (Optional) The directory in which to run the code. + - `timeout`: (Optional) Maximum duration in milliseconds to wait for the code execution to complete. + - `exitCodes`: (Optional) An array of acceptable exit codes. If the code execution's exit code is not in this list, the step fails (default: `[0]`). + - `stdio`: (Optional) A string or regular expression to validate against the code's combined stdout and stderr. If the output doesn't match, the step fails. Regex must start and end with `/` (e.g., `/^hello world.*/`). + - *Output Saving:* You can also save the code's output using `path`, `directory`, `maxVariation`, and `overwrite` properties. See the [`runCode`](/docs/references/schemas/runCode) reference for details. + +**Setting Variables:** To capture output into variables for later steps, use the step-level `variables` object. You can assign values based on the code's output using expressions like `$$stdio.stdout`, `$$stdio.stderr`, or `$$exitCode`. + +> For comprehensive options, see the [`runCode`](/docs/references/schemas/runCode) reference. + +## Examples + +Here are a few ways you might use the `runCode` action: + +### Run a simple Python script + +This example prints "hello world" using Python. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Run a simple Python script.", + "runCode": { + "language": "python", + "code": "print('hello world')" + } + } + ] + } + ] +} +``` + +### Run JavaScript code with expected output validation + +This example runs a simple Node.js script and checks the output. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Run JS code and check the output.", + "runCode": { + "language": "javascript", + "code": "console.log('Hello from Node!');", + "stdio": "Hello from Node!" + } + } + ] + } + ] +} +``` + +### Test a failure condition using exit codes (Bash) + +This example runs a Bash command (`false`) via `runCode` and checks that the exit code is `1`. Because the command is expected to fail with exit code 1, the step passes. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Run a failing Bash command and expect exit code 1.", + "runCode": { + "language": "bash", + "code": "false", + "exitCodes": [1] + } + } + ] + } + ] +} +``` + +### Set a variable based on code output (Python) + +The first step runs Python code to print "setup", validates the output using a regex, and captures the full stdout into a variable named `SETUP_VAL`. The second step uses Bash via `runCode` to echo the content of the `SETUP_VAL` variable and validates that the output is indeed "setup". + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Set a variable based on Python output.", + "runCode": { + "language": "python", + "code": "import sys; sys.stdout.write('setup')", + "stdio": "/.+/" + }, + "variables": { + "SETUP_VAL": "$$stdio.stdout" + } + }, + { + "description": "Echo and validate the variable using Bash.", + "runCode": { + "language": "bash", + "code": "echo $SETUP_VAL", + "stdio": "setup" + } + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/runShell.md b/docs/get-started/actions/runShell.md index e5869611..bcecde15 100644 --- a/docs/get-started/actions/runShell.md +++ b/docs/get-started/actions/runShell.md @@ -11,22 +11,29 @@ description: Perform a native shell command. The `runShell` action runs a shell command or script on the local machine and evaluates the results, extending Doc Detective's testing capabilities to anything you can script. -> For comprehensive options, see the [`runShell`](/docs/references/schemas/runShell) reference. - -`runShell` uses your device's native shell (`cmd` on Windows, `bash` on macOS and Linux) to execute the command. The command is executed in the context of the current user. - -`runShell` can evaluate commands in two ways: +`runShell` uses your device's native shell (`cmd` on Windows, `bash` or other default shell on macOS/Linux) to execute the command in the context of the current user. + +You can specify the command directly as a string or use an object for more options: + +- **String Shorthand:** Provide the command string directly as the value for the `runShell` key. +- **Object Format:** Use an object with the following properties: + - `command`: (Required) The command or script to execute. + - `args`: (Optional) An array of arguments to pass to the command. + - `workingDirectory`: (Optional) The directory in which to run the command. + - `timeout`: (Optional) Maximum duration in milliseconds to wait for the command to complete. + - `exitCodes`: (Optional) An array of acceptable exit codes. If the command's exit code is not in this list, the step fails (default: `[0]`). + - `stdio`: (Optional) A string or regular expression to validate against the command's combined stdout and stderr. If the output doesn't match, the step fails. Regex must start and end with `/` (e.g., `/^hello world.*/`). + - *Output Saving:* You can also save the command's output using `path`, `directory`, `maxVariation`, and `overwrite` properties. See the [`runShell`](/docs/references/schemas/runShell) reference for details. + +**Setting Variables:** To capture output into variables for later steps, use the step-level `variables` object. You can assign values based on the code's output using expressions like `$$stdio.stdout`, `$$stdio.stderr`, or `$$exitCode`. -- **Exit code**: The command's exit code is checked against a list of expected exit codes set in the `exitCodes` parameter. If the command's exit code exists in the list of expected codes, the step passes. `exitCodes` defaults to `[0]`. You can specify non-zero exit codes to test for failure conditions. -- **Output**: If the expected output (as set in the `output` parameter) exists in the command's actual output (both stdout and stderr), the step passes. You can specify expected output as a string or a regular expression. To use a regular expression, the string must start and end with a forward slash, like in `/^hello world.*/`. - -You can also set variables based on the command's output with the `setVariables` parameter. This is useful for capturing the output of a command and using it in subsequent steps. Each variable is set based on a regular expression match of the command's output. +> For comprehensive options, see the [`runShell`](/docs/references/schemas/runShell) reference. ## Examples Here are a few ways you might use the `runShell` action: -### Run a simple command +### Run a simple command (string shorthand) This example prints "hello world" to the output. @@ -36,9 +43,28 @@ This example prints "hello world" to the output. { "steps": [ { - "action": "runShell", "description": "Run a simple command.", - "command": "echo 'hello world'" + "runShell": "echo 'hello world'" + } + ] + } + ] +} +``` + +### Run a command with arguments (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Run echo with arguments.", + "runShell": { + "command": "echo", + "args": ["hello", "world"] + } } ] } @@ -46,7 +72,7 @@ This example prints "hello world" to the output. } ``` -### Run a command with expected output +### Run a command with expected output validation This example runs a Docker container and checks the output for a specific string. @@ -56,10 +82,11 @@ This example runs a Docker container and checks the output for a specific string { "steps": [ { - "action": "runShell", "description": "Run a Docker container and check the output.", - "command": "docker run hello-world", - "output": "Hello from Docker!" + "runShell": { + "command": "docker run hello-world", + "stdio": "Hello from Docker!" + } } ] } @@ -67,9 +94,9 @@ This example runs a Docker container and checks the output for a specific string } ``` -### Test a failure condition +### Test a failure condition using exit codes -This example runs a failing command and checks the exit code. Because the command is expected to fail with an exit code `1`, the step passes. +This example runs a failing command (`false`) and checks that the exit code is `1`. Because the command is expected to fail with exit code 1, the step passes. ```json { @@ -77,10 +104,11 @@ This example runs a failing command and checks the exit code. Because the comman { "steps": [ { - "action": "runShell", - "description": "Run a failing command.", - "command": "false", - "exitCodes": [1] + "description": "Run a failing command and expect exit code 1.", + "runShell": { + "command": "false", + "exitCodes": [1] + } } ] } @@ -90,7 +118,7 @@ This example runs a failing command and checks the exit code. Because the comman ### Set a variable based on command output -The first step echoes "setup", validates that it outputs a string or one or more characters, and sets a variable based on the output. The next step echoes the variable, then validates that the command output "setup". +The first step echoes "setup", validates the output using a regex, and captures the full stdout into a variable named `TEST`. The second step echoes the content of the `TEST` variable and validates that the output is indeed "setup". ```json { @@ -98,22 +126,21 @@ The first step echoes "setup", validates that it outputs a string or one or more { "steps": [ { - "action": "runShell", "description": "Set a variable based on command output.", - "command": "echo setup", - "output": "/.+/", - "setVariables": [ - { - "name": "TEST", - "regex": ".*" - } - ] + "runShell": { + "command": "echo setup", + "stdio": "/.+/" + }, + "variables": { + "TEST": "$$stdio.stdout" + } }, { - "action": "runShell", "description": "Echo and validate the variable.", - "command": "echo $TEST", - "output": "setup" + "runShell": { + "command": "echo $TEST", + "stdio": "setup" + } } ] } diff --git a/docs/get-started/actions/saveScreenshot.md b/docs/get-started/actions/saveScreenshot.md deleted file mode 100644 index 776c97d7..00000000 --- a/docs/get-started/actions/saveScreenshot.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: saveScreenshot -layout: default -nav_order: 1 -parent: Actions -grand_parent: Tests -description: Take a screenshot in PNG format. ---- - -# saveScreenshot - -The `saveScreenshot` action captures a PNG of the current viewport. If an image with the same name and dimensions exists, it can also perform pixel diffs and capture updated screenshots for debugging or media updating purposes. - -> For comprehensive options, see the [`saveScreenshot`](/docs/references/schemas/saveScreenshot) reference. diff --git a/docs/get-started/actions/screenshot.md b/docs/get-started/actions/screenshot.md new file mode 100644 index 00000000..6ffe9ac1 --- /dev/null +++ b/docs/get-started/actions/screenshot.md @@ -0,0 +1,148 @@ +--- +title: screenshot +layout: default +nav_order: 1 # Adjust nav_order as needed +parent: Actions +grand_parent: Tests +description: Take a screenshot in PNG format and optionally perform visual regression testing. +--- + +# screenshot + +The `screenshot` action captures a PNG image of the current browser viewport or a specific element. It can also perform visual regression testing by comparing the captured image against a previously saved reference image. + +You can specify the screenshot action in several ways: + +- **Boolean Shorthand:** Set `screenshot: true` to capture the full viewport with default settings. +- **String Shorthand:** Provide a file path directly as the value for the `screenshot` key (e.g., `screenshot: "my_screenshot.png"`). This saves the full viewport screenshot to the specified path. +- **Object Format:** Use an object for more control over the path, visual comparison, and cropping: + - `path`: (Optional) The file path where the screenshot will be saved. Can include directories. If omitted, a default path is generated. + - `directory`: (Optional) The directory where the screenshot will be saved. If `path` includes a directory, this is ignored. Defaults to the configured output directory. + - `maxVariation`: (Optional) The maximum acceptable visual difference (0 to 1) when comparing against a reference image. If the variation exceeds this threshold, the step fails. If set, visual comparison is enabled. + - `overwrite`: (Optional) Controls when to overwrite the reference image. Options: + - `never`: Never overwrite the reference image (default). + - `always`: Always overwrite the reference image with the new capture. + - `aboveVariation`: Overwrite only if the visual difference exceeds `maxVariation`. + - `crop`: (Optional) Crop the screenshot to a specific element. Provide a CSS/XPath selector string (e.g., `"#myElement"`) or an object: + - `selector`: (Required in object) The CSS or XPath selector of the element to crop to. + - `elementText`: (Optional) Display text to further specify the element if the selector isn't unique. + - `padding`: (Optional) Add padding around the element before cropping. Can be a single number (all sides) or an object `{ top, right, bottom, left }`. + +**Visual Regression Testing:** If `maxVariation` is set and a reference image exists at the target `path`, the action compares the new screenshot to the reference. If the difference is within `maxVariation`, the step passes. If it exceeds the threshold, the step fails, and behavior depends on the `overwrite` setting. + +> For comprehensive options, see the [`screenshot`](/docs/references/schemas/screenshot) reference. + +## Examples + +Here are a few ways you might use the `screenshot` action: + +### Simple full viewport screenshot (boolean shorthand) + +Saves to a default path like `output/screenshot_1745032062266.png`. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Take a simple screenshot.", + "screenshot": true + } + ] + } + ] +} +``` + +### Full viewport screenshot to specific path (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Save screenshot to a specific file.", + "screenshot": "./output/images/login_page.png" + } + ] + } + ] +} +``` + +### Full viewport screenshot with visual comparison (object format) + +Compares against `./output/reference_images/home_page.png`. Fails if difference > 5% (`0.05`). Overwrites reference if failure occurs. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Take screenshot and compare to reference.", + "screenshot": { + "path": "home_page.png", + "directory": "./output/reference_images", + "maxVariation": 0.05, + "overwrite": "aboveVariation" + } + } + ] + } + ] +} +``` + +### Screenshot cropped to an element (object format) + +Captures only the element matching the `#main-content` selector. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Screenshot only the main content area.", + "screenshot": { + "path": "main_content_area.png", + "directory": "./output/element_shots", + "crop": "#main-content" + } + } + ] + } + ] +} +``` + +### Screenshot cropped to an element with padding and visual comparison + +Captures the element with text "Submit Button", adds 10px padding, and compares against a reference image. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Screenshot a button with padding and compare.", + "screenshot": { + "path": "submit_button_padded.png", + "directory": "./output/reference_elements", + "crop": { + "elementText": "Submit Button", + "padding": 10 + }, + "maxVariation": 0.01, + "overwrite": "aboveVariation" + } + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/setVariables.md b/docs/get-started/actions/setVariables.md deleted file mode 100644 index dc5904a6..00000000 --- a/docs/get-started/actions/setVariables.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: setVariables -layout: default -nav_order: 1 -parent: Actions -grand_parent: Tests -description: Load environment variables from a .env file. ---- - -# setVariables - -The `setVariables` action sets environment variables from a `.env` file. This action is useful for accessing sensitive information, such as API keys or other credentials, without hardcoding them into your tests. - -> For comprehensive options, see the [`setVariables`](/docs/references/schemas/setVariables) reference. - -## Example - -```json -{ - "tests": [ - { - "steps": [ - { - "description": "Set environment variables from a .env file.", - "action": "setVariables", - "path": "./secrets.env" - } - ] - } - ] -} -``` diff --git a/docs/get-started/actions/startRecording.md b/docs/get-started/actions/startRecording.md deleted file mode 100644 index e5491b89..00000000 --- a/docs/get-started/actions/startRecording.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: startRecording -layout: default -nav_order: 1 -parent: Actions -grand_parent: Tests -description: Capture a video of test execution. ---- - -# startRecording - -The `startRecording` action records tests as they are run for debugging or multimedia purposes. - -> For comprehensive options, see the [`startRecording`](/docs/references/schemas/startRecording) reference. diff --git a/docs/get-started/actions/stopRecord.md b/docs/get-started/actions/stopRecord.md new file mode 100644 index 00000000..d62c6bea --- /dev/null +++ b/docs/get-started/actions/stopRecord.md @@ -0,0 +1,47 @@ +--- +title: stopRecord +layout: default +nav_order: 1 +parent: Actions +grand_parent: Tests +description: Stop capturing a video of test execution. +--- + +# stopRecord + +The `stopRecord` action stops a video recording previously started by a [`record`](record) action and saves the video file. + +This action takes a simple boolean value: + +- `stopRecord: true`: Stops the current recording. + +>Note: You must include a `stopRecord: true` step to finalize and save any recording started with the `record` action. +> +> For comprehensive options, see the [`stopRecord`](/docs/references/schemas/stopRecord) reference. + +## Example + +This example starts recording, performs an action, and then stops the recording. + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Start recording.", + "record": "./output/session_video.mp4" + }, + { + "description": "Navigate to the site.", + "goTo": "https://example.com" + }, + { + "description": "Stop the recording.", + "stopRecord": true + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/stopRecording.md b/docs/get-started/actions/stopRecording.md deleted file mode 100644 index 2578b392..00000000 --- a/docs/get-started/actions/stopRecording.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: stopRecording -layout: default -nav_order: 1 -parent: Actions -grand_parent: Tests -description: Stop capturing a video of test execution. ---- - -# stopRecording - -The `stopRecording` action stops a recording started by a [`startRecording`](startRecording) action. - -> For comprehensive options, see the [`stopRecording`](/docs/references/schemas/stopRecording) reference. diff --git a/docs/get-started/actions/type.md b/docs/get-started/actions/type.md new file mode 100644 index 00000000..6b916374 --- /dev/null +++ b/docs/get-started/actions/type.md @@ -0,0 +1,193 @@ +--- +title: type +layout: default +nav_order: 1 +parent: Actions +grand_parent: Tests +description: Type keys, including special keys like Enter. +--- + +# type + +The `type` action simulates key presses, including special keys such as Enter. This action is useful for simulating user input, such as filling out a form, navigating a website, or using keyboard shortcuts. It typically requires a preceding `find` or `click` action to focus an input element. + +You can specify the keys to type in several ways: + +- **String Shorthand:** Provide a simple string as the value for the `type` key. +- **Array Shorthand:** Provide an array of strings. Each string in the array is typed sequentially. This allows mixing regular text with [special keys](#special-keys). +- **Object Format:** Use an object for more control: + - `keys`: (Required) A string or an array of strings representing the keys to type. + - `inputDelay`: (Optional) Delay in milliseconds between each key press. Useful for making recordings more legible. + +> For comprehensive options, see the [`type`](/docs/references/schemas/type) reference. + +## Special keys + +You can use special keys in the `keys` field to simulate non-character key presses. To use a special key, use the key's associated code enclosed in `$` symbols. For example, to simulate pressing the Enter key, use `$ENTER$`. + +Here's a list of special keys you can use: + +| Key | Code | +| -------------- | ------------------ | +| Alt | $ALT$ | +| Backspace | $BACKSPACE$ | +| Cancel | $CANCEL$ | +| Clear | $CLEAR$ | +| Command | $COMMAND$ | +| Control | $CTRL$ | +| Delete | $DELETE$ | +| End | $END$ | +| Enter | $ENTER$ | +| Escape | $ESCAPE$ | +| Help | $HELP$ | +| Home | $HOME$ | +| Insert | $INSERT$ | +| NULL | $NULL$ | +| Page Down | $PAGE_DOWN$ | +| Page Up | $PAGE_UP$ | +| Pause | $PAUSE$ | +| Return | $RETURN$ | +| Shift | $SHIFT$ | +| Space | $SPACE$ | +| Tab | $TAB$ | +| ZenkakuHankaku | $ZANKAKU_HANDKAKU$ | +| Arrow Down | $ARROW_DOWN$ | +| Arrow Left | $ARROW_LEFT$ | +| Arrow Right | $ARROW_RIGHT$ | +| Arrow Up | $ARROW_UP$ | +| Numpad 0 | $NUMPAD_0$ | +| Numpad 1 | $NUMPAD_1$ | +| Numpad 2 | $NUMPAD_2$ | +| Numpad 3 | $NUMPAD_3$ | +| Numpad 4 | $NUMPAD_4$ | +| Numpad 5 | $NUMPAD_5$ | +| Numpad 6 | $NUMPAD_6$ | +| Numpad 7 | $NUMPAD_7$ | +| Numpad 8 | $NUMPAD_8$ | +| Numpad 9 | $NUMPAD_9$ | +| : | $SEMICOLON$ | +| = | $EQUALS$ | +| \* | $MULTIPLY$ | +| + | $ADD$ | +| \| | $SEPARATOR$ | +| - | $SUBSTRACT$ | +| . | $DECIMAL$ | +| / | $DIVIDE$ | +| F1 | $F1$ | +| F2 | $F2$ | +| F3 | $F3$ | +| F4 | $F4$ | +| F5 | $F5$ | +| F6 | $F6$ | +| F7 | $F7$ | +| F8 | $F8$ | +| F9 | $F9$ | +| F10 | $F10$ | +| F11 | $F11$ | +| F12 | $F12$ | + +## Examples + +### Perform a search (array shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Go to Google", + "goTo": "https://www.google.com" + }, + { + "description": "Find and click the search bar", + "find": { + "selector": "[title=Search]", + "click": true + } + }, + { + "description": "Type search query and press Enter", + "type": ["American Shorthair kittens", "$ENTER$"] + } + ] + } + ] +} +``` + +### Fill credentials from environment variables (string shorthand) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Load credentials", + "loadVariables": ".env" + }, + { + "description": "Go to login page", + "goTo": "https://console.acme.com/login" + }, + { + "description": "Find, click, and type username", + "find": { + "selector": "#username", + "click": true, + "type": "$USERNAME" // Type directly within find + } + }, + { + "description": "Find, click, and type password, then press Enter", + "find": { + "selector": "#password", + "click": true, + "type": ["$PASSWORD", "$ENTER$"] // Type array within find + } + } + ] + } + ] +} +``` + +### Type with an increased delay (object format) + +```json +{ + "tests": [ + { + "steps": [ + { + "description": "Go to Google", + "goTo": "https://www.google.com" + }, + { + "description": "Start recording", + "record": "recording.webm" + }, + { + "description": "Find and click search bar", + "find": { + "selector": "[title=Search]", + "click": true + } + }, + { + "description": "Type slowly and press Enter", + "type": { + "keys": ["American Shorthair kittens", "$ENTER$"], + "inputDelay": 500 + } + }, + { + "description": "Stop recording", + "stopRecord": true + } + ] + } + ] +} +``` diff --git a/docs/get-started/actions/typeKeys.md b/docs/get-started/actions/typeKeys.md deleted file mode 100644 index ff816211..00000000 --- a/docs/get-started/actions/typeKeys.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -title: typeKeys -layout: default -nav_order: 1 -parent: Actions -grand_parent: Tests -description: Type keys. ---- - -# typeKeys - -The `typeKeys` action registers key presses, including special keys such as Enter. This action is useful for simulating user input, such as filling out a form, navigating a website, or using keyboard shortcuts. - -You can specify `keys` either as a string or as an array of strings. Each character in a string or array is separated is treated as a separate key press, but specifying an array allows you to include both regular and [special](#special-keys) keys. - -> For comprehensive options, see the [`typeKeys`](/docs/references/schemas/typeKeys) reference. - -## Special keys - -You can use special keys in the `keys` field to simulate key presses. To use a special key, use the key's associated code. For example, to simulate pressing the Enter key, use `$Enter$`. - -Here's a list of special keys you can use: - -| Key | Code | -| -------------- | ------------------ | -| Alt | $ALT$ | -| Backspace | $BACKSPACE$ | -| Cancel | $CANCEL$ | -| Clear | $CLEAR$ | -| Command | $COMMAND$ | -| Control | $CTRL$ | -| Delete | $DELETE$ | -| End | $END$ | -| Enter | $ENTER$ | -| Escape | $ESCAPE$ | -| Help | $HELP$ | -| Home | $HOME$ | -| Insert | $INSERT$ | -| NULL | $NULL$ | -| Page Down | $PAGE_DOWN$ | -| Page Up | $PAGE_UP$ | -| Pause | $PAUSE$ | -| Return | $RETURN$ | -| Shift | $SHIFT$ | -| Space | $SPACE$ | -| Tab | $TAB$ | -| ZenkakuHankaku | $ZANKAKU_HANDKAKU$ | -| Arrow Down | $ARROW_DOWN$ | -| Arrow Left | $ARROW_LEFT$ | -| Arrow Right | $ARROW_RIGHT$ | -| Arrow Up | $ARROW_UP$ | -| Numpad 0 | $NUMPAD_0$ | -| Numpad 1 | $NUMPAD_1$ | -| Numpad 2 | $NUMPAD_2$ | -| Numpad 3 | $NUMPAD_3$ | -| Numpad 4 | $NUMPAD_4$ | -| Numpad 5 | $NUMPAD_5$ | -| Numpad 6 | $NUMPAD_6$ | -| Numpad 7 | $NUMPAD_7$ | -| Numpad 8 | $NUMPAD_8$ | -| Numpad 9 | $NUMPAD_9$ | -| : | $SEMICOLON$ | -| = | $EQUALS$ | -| \* | $MULTIPLY$ | -| + | $ADD$ | -| \| | $SEPARATOR$ | -| - | $SUBSTRACT$ | -| . | $DECIMAL$ | -| / | $DIVIDE$ | -| F1 | $F1$ | -| F2 | $F2$ | -| F3 | $F3$ | -| F4 | $F4$ | -| F5 | $F5$ | -| F6 | $F6$ | -| F7 | $F7$ | -| F8 | $F8$ | -| F9 | $F9$ | -| F10 | $F10$ | -| F11 | $F11$ | -| F12 | $F12$ | - -## Examples - -### Perform a search - -```json -{ - "tests": [ - { - "steps": [ - { - "action": "goTo", - "url": "https://www.google.com" - }, - { - "action": "find", - "selector": "[title=Search]", - "click": true - }, - { - "action": "typeKeys", - "keys": ["American Shorthair kittens", "$ENTER$"] - } - ] - } - ] -} -``` - -### Fill credentials from environment variables - -```json -{ - "tests": [ - { - "steps": [ - { - "action": "setVariables", - "path": ".env" - }, - { - "action": "goTo", - "url": "https://console.acme.com/login" - }, - { - "action": "find", - "selector": "#username", - "click": true - }, - { - "action": "typeKeys", - "keys": ["$USERNAME"] - }, - { - "action": "find", - "selector": "#password", - "click": true - }, - { - "action": "typeKeys", - "keys": ["$PASSWORD", "$ENTER$"] - } - ] - } - ] -} -``` - -### Type with an increased delay for legibility in recordings - -```json -{ - "tests": [ - { - "steps": [ - { - "action": "goTo", - "url": "https://www.google.com" - }, - { - "action": "startRecording", - "path": "recording.webm" - }, - { - "action": "find", - "selector": "[title=Search]", - "click": true - }, - { - "action": "typeKeys", - "keys": ["American Shorthair kittens", "$ENTER$"], - "delay": 500 - }, - { - "action": "stopRecording" - } - ] - } - ] -} -``` diff --git a/docs/get-started/actions/wait.md b/docs/get-started/actions/wait.md index fdf0fe70..fb161a63 100644 --- a/docs/get-started/actions/wait.md +++ b/docs/get-started/actions/wait.md @@ -9,25 +9,63 @@ description: Pause before performing the next action. # wait -The `wait` action pauses before performing the next step. This action is useful for waiting a set duration before continuing a test, such as creating a pause before ending a recording. +The `wait` action pauses test execution for a specified duration before proceeding to the next step. This is useful for adding delays, for example, to allow time for animations to complete or to make recordings easier to follow. -You can specify the `duration` to pause in milliseconds. The default `duration` is 5 seconds. +The value assigned to the `wait` key is the duration to pause in milliseconds. You can provide a number directly. > For comprehensive options, see the [`wait`](/docs/references/schemas/wait) reference. ## Examples +### Wait for 3 seconds + ```json { - "description": "Wait for 5 seconds.", - "action": "wait" + "tests": [ + { + "steps": [ + { + "description": "Perform an action.", + "goTo": "https://example.com" + }, + { + "description": "Wait for 3000 milliseconds (3 seconds).", + "wait": 3000 + }, + { + "description": "Perform the next action.", + "screenshot": "after_wait.png" + } + ] + } + ] } ``` +### Wait for 500 milliseconds + ```json { - "description": "Wait for 500 milliseconds.", - "action": "wait", - "duration": 500 + "tests": [ + { + "steps": [ + { + "description": "Click something.", + "find": { + "selector": "#myButton", + "click": true + } + }, + { + "description": "Wait for half a second.", + "wait": 500 + }, + { + "description": "Check the result.", + "find": "Action complete!" + } + ] + } + ] } ``` diff --git a/docs/get-started/concepts.md b/docs/get-started/concepts.md index 0859c9bf..5f049213 100644 --- a/docs/get-started/concepts.md +++ b/docs/get-started/concepts.md @@ -22,19 +22,20 @@ A step is a portion of a test that includes a single action. Conceptually parall An action is the task performed in a step. Doc Detective supports a variety of actions: -| Name | Description | -| :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [checkLink](/docs/get-started/actions/checkLink.md) | Check if a URL returns an acceptable status code from a GET request. | -| [find](/docs/get-started/actions/find.md) | Locate and interact with an element on the page. | -| [goTo](/docs/get-started/actions/goTo.md) | Navigate to a specified URL. | -| [httpRequest](/docs/get-started/actions/httpRequest.md) | Perform a generic HTTP request, for example to an API. | -| [runShell](/docs/get-started/actions/runShell.md) | Perform a native shell command. | -| [saveScreenshot](/docs/get-started/actions/saveScreenshot.md) | Take a screenshot in PNG format. | -| [setVariables](/docs/get-started/actions/setVariables.md) | Load environment variables from a `.env` file. | -| [startRecording](/docs/get-started/actions/startRecording.md) | Capture a video of test run. | -| [stopRecording](/docs/get-started/actions/stopRecording.md) | Stop capturing a video of test run. | -| [typeKeys](/docs/get-started/actions/typeKeys.md) | Type keys. To type special keys, begin and end the string with `$` and use the special key’s enum. For example, to type the Escape key, enter `$ESCAPE$`. | -| [wait](/docs/get-started/actions/wait.md) | Pause before performing the next action. | +| Name | Description | +| :---------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [checkLink](/docs/get-started/actions/checkLink.md) | Check if a URL returns an acceptable status code from a GET request. | +| [find](/docs/get-started/actions/find.md) | Locate and interact with an element on the page. | +| [click](/docs/get-started/actions/click.md) | Click an element. | +| [goTo](/docs/get-started/actions/goTo.md) | Navigate to a specified URL. | +| [httpRequest](/docs/get-started/actions/httpRequest.md) | Perform a generic HTTP request, for example to an API. | +| [runShell](/docs/get-started/actions/runShell.md) | Perform a native shell command. | +| [screenshot](/docs/get-started/actions/screenshot.md) | Take a screenshot in PNG format. | +| [loadVariables](/docs/get-started/actions/loadVariables.md) | Load environment variables from a `.env` file. | +| [record](/docs/get-started/actions/record.md) | Capture a video of test run. | +| [stopRecord](/docs/get-started/actions/stopRecord.md) | Stop capturing a video of test run. | +| [type](/docs/get-started/actions/type.md) | Type keys. To type special keys, begin and end the string with `$` and use the special key’s enum. For example, to type the Escape key, enter `$ESCAPE$`. | +| [wait](/docs/get-started/actions/wait.md) | Pause before performing the next action. | ## Context diff --git a/docs/get-started/config/contexts/index.md b/docs/get-started/config/contexts/index.md index 8f3e892b..21ffdf37 100644 --- a/docs/get-started/config/contexts/index.md +++ b/docs/get-started/config/contexts/index.md @@ -3,16 +3,18 @@ title: Contexts layout: default nav_order: 1 parent: Configuration -description: A set of conditions that must be met for a test to run. +description: Define the contexts (platform and browser combinations) where tests should run. --- # Contexts -Doc Detective uses contexts to determine which tests to run. A context is a set of conditions that must be met in order for a test to run. For example, a context might specify that a test should only run in Safari on macOS. +Doc Detective uses contexts to determine *where* tests should run. A context defines a combination of a target platform (operating system) and, optionally, a target browser with specific configurations. -By default, Doc Detective runs tests in Chrome on Windows, macOS, and Linux. You can specify custom contexts to run tests in other apps. +By default, if contexts are needed but not specified, Doc Detective attempts to find a supported browser (like Chrome or Firefox) on the current platform (Windows, macOS, or Linux) and run tests there. -Each context is made up of an `app` object and a `platforms` array. When Doc Detective runs tests, it checks the associated contexts to see if the app is available and if it's running on a specified platform. If the conditions are met, the test runs in that context. You can specify multiple contexts for a test, and Doc Detective will run the test in each context that is met. +You define contexts using an array of context objects. Each context object specifies the target `platforms` (as a string or array) and the target `browsers` (as a string, array, or object). + +When Doc Detective runs tests, it evaluates the defined contexts against the current environment. If the current platform matches one specified in a context, and if a browser is specified and available, the test runs in that specific browser on that platform. You can specify multiple contexts, and Doc Detective will attempt to run the relevant tests in each matching context. For comprehensive options, see the [context](/docs/references/schemas/context) reference. @@ -20,375 +22,293 @@ For comprehensive options, see the [context](/docs/references/schemas/context) r You can specify contexts at three different levels, in order of precedence: -- **Config**: You can specify contexts in the [`config`](/docs/references/schemas/config) object. These contexts apply to all tests in the suite. -- **Spec**: You can specify contexts in a [`specification`](/docs/references/schemas/specification) object. These contexts override config-level contexts and apply to all tests in the spec. -- **Test**: You can specify contexts in a [`test`](/docs/references/schemas/test) object. These contexts override config- and spec-level contexts and apply only to that test. +- **Config**: Contexts defined in the main [`config`](/docs/references/schemas/config) apply to all tests unless overridden. +- **Spec**: Contexts defined in a [`specification`](/docs/references/schemas/specification) override config-level contexts and apply to all tests within that spec unless overridden. +- **Test**: Contexts defined within a specific [`test`](/docs/references/schemas/test) override config- and spec-level contexts and apply only to that test. -When you specify contexts, you use a `contexts` array. For example, the following JSON specifies three contexts: +Contexts are defined using a `runOn` array containing context objects. For example: ```json { ... - "contexts": [ + "runOn": [ { - "app": { - "name": "chrome" - }, - "platforms": ["windows","mac","linux"] + "platforms": ["windows", "mac", "linux"], + "browsers": "chrome" }, { - "app": { - "firefox" - }, - "platforms": ["windows","mac","linux"] + "platforms": ["windows", "mac", "linux"], + "browsers": "firefox" }, { - "app": { - "name": "safari" - }, - "platforms": ["mac"] + "platforms": "mac", + "browsers": "webkit" // or "safari" } ], ... } ``` -## Apps +## Browsers + +Doc Detective can perform browser-based tests on several browser engines. The following browser names are supported in the `browsers` property: -Doc Detective can perform tests on a variety of apps. The following apps are supported: +- **Chrome** (`chrome`): Uses Chromium. +- **Firefox** (`firefox`): Uses Firefox. +- **WebKit** (`webkit`): Uses WebKit. The name `safari` can be used as an alias for `webkit`. -- [Chrome](#chrome) (`chrome`) -- [Firefox](#firefox) (`firefox`) -- [Safari](#safari) (`safari`) -- [Edge](#edge) (`edge`) -### Chrome +### Chrome (`chrome`) -Chrome is available on Windows, macOS, and Linux. Doc Detective manages and runs a Chrome instance internally, so you don't need to install anything extra. +Available on Windows, macOS, and Linux. -Chrome is the only browser that supports recording test runs with the [`startRecording`](/docs/references/schemas/startRecording) action. +Chrome is the only browser that currently supports video recording via the [`record`](/docs/get-started/actions/record) action. -Here's a basic Chrome context: +Here's a basic Chrome context for all platforms: ```json { - "app": { - "name": "chrome" - }, - "platforms": ["windows", "mac", "linux"] + "platforms": ["windows", "mac", "linux"], + "browsers": "chrome" } ``` -#### Dimensions and visibility - -You can specify the browser dimensions and visibility (`headless`) during tests. `headless` must be `false` to record test runs. +Or using the object format: ```json { - "app": { - "name": "chrome", - "options": { - "width": 1024, - "height": 768, - "headless": false - } - }, - "platforms": ["windows", "mac", "linux"] + "platforms": ["windows", "mac", "linux"], + "browsers": { + "name": "chrome" + } } ``` -#### Custom path +#### Chrome Dimensions and Visibility -You can specify a Chrome installation on your system if you want to use a specific version of Chrome or a Chromium derivative. If you specify a custom path, you must also specify a path to a matching ChromeDriver executable. For example: +You can specify browser window dimensions, viewport dimensions, and visibility (`headless`). `headless` must be `false` (i.e., run in headed mode) to use the `record` action. ```json { - "app": { + "platforms": ["windows", "mac", "linux"], + "browsers": { "name": "chrome", - "options": { - "path": "/path/to/chrome", - "driverPath": "/path/to/chromedriver" + "headless": false, // Required for recording + "window": { + "width": 1280, + "height": 800 + }, + "viewport": { + "width": 1200, + "height": 720 } - }, - "platforms": ["windows", "mac", "linux"] + } } ``` -### Firefox +### Firefox (`firefox`) -Firefox is available on Windows, macOS, and Linux. Doc Detective manages and runs a Firefox instance internally, so you don't need to install anything extra. +Available on Windows, macOS, and Linux. Here's a basic Firefox context: ```json { - "app": { - "name": "firefox" - }, - "platforms": ["windows", "mac", "linux"] -} -``` - -#### Dimensions and visibility - -You can specify the browser dimensions and visibility (`headless`) during tests. - -```json -{ - "app": { - "name": "chrome", - "options": { - "width": 1024, - "height": 768, - "headless": false - } - }, - "platforms": ["windows", "mac", "linux"] + "platforms": ["windows", "mac", "linux"], + "browsers": "firefox" } ``` -#### Custom path +#### Firefox Dimensions and Visibility -You can specify a Firefox installation on your system if you want to use a specific version of Firefox or a Firefox derivative. For example: +You can specify dimensions and visibility (`headless`). ```json { - "app": { + "platforms": ["windows", "mac", "linux"], + "browsers": { "name": "firefox", - "options": { - "path": "/path/to/firefox" + "headless": true, + "window": { + "width": 1024, + "height": 768 } - }, - "platforms": ["windows", "mac", "linux"] + } } ``` -### Safari - -Safari is only available on macOS. Doc Detective runs tests in a sandboxed instance of your local Safari browser. - -Before you run tests on Safari, you need to enable SafariDriver with the following command in a terminal: - -```bash -safaridriver --enable -``` - -**Note:** SafariDriver is enabled by default in GitHub Actions. - -If Doc Detective isn't running tests in Safari, make sure +### WebKit (`webkit` or `safari`) -- SafariDriver is enabled. -- the **Enable automation** option is selected the Safari's **Develop** menu. +WebKit testing is primarily associated with Safari on macOS. Doc Detective runs tests using the WebKit driver. -#### Dimensions +You can use either `webkit` or `safari` as the browser name. -You can specify the browser dimensions during tests. +Before running tests with WebKit/Safari on macOS, you might need to enable the driver: -**Note:** Safari doesn't support headless mode. +1. Run `safaridriver --enable` in your terminal. +2. Ensure **Develop > Allow Remote Automation** is checked in Safari's menu bar (you might need to enable the Develop menu first in Safari's Advanced preferences). -```json -{ - "app": { - "name": "safari", - "options": { - "width": 1024, - "height": 768 - } - }, - "platforms": ["mac"] -} -``` - -### Edge +*Note: This setup is often handled automatically in CI environments like GitHub Actions.* -Edge is available on Windows, macOS, and Linux. edge is installed by default on Windows, but you must manually install it on macOS and Linux. If Edge is installed, Doc Detective can automatically detect and run tests in your local installation. - -Here's a basic Edge context: +Here's a basic WebKit/Safari context for macOS: ```json { - "app": { - "name": "edge" - }, - "platforms": ["windows", "mac", "linux"] + "platforms": "mac", + "browsers": "webkit" // or "safari" } ``` -#### Dimensions and visibility +#### WebKit/Safari Dimensions -You can specify the browser dimensions and visibility (`headless`) during tests. +You can specify window or viewport dimensions. WebKit/Safari does **not** support headless mode. ```json { - "app": { - "name": "edge", - "options": { - "width": 1024, - "height": 768, - "headless": false + "platforms": "mac", + "browsers": { + "name": "webkit", // or "safari" + "headless": false, // Headless is not supported + "viewport": { + "width": 1440, + "height": 900 } - }, - "platforms": ["windows", "mac", "linux"] + } } ``` ## Platforms -Doc Detective can perform tests on a variety of platforms. The following platforms are supported: +Doc Detective can run tests targeting the following platforms: - Windows (`windows`) - macOS (`mac`) -- Linux (tested on Ubuntu) (`linux`) +- Linux (`linux`) (Tested primarily on Ubuntu) -When you specify a platform for a context, Doc Detective attempts to run associated tests when the context is executed on that platform. If a platform isn't specified, Doc Detective attempts to run the tests on all platforms. +When you specify a platform (or multiple platforms) in a context, Doc Detective attempts to run the associated tests only when executed on a matching operating system. If `platforms` is omitted, it defaults to the current platform. -For example, the following context specifies that tests should only run on macOS: +For example, this context targets only macOS: ```json { - "app": { - "name": "chrome" - }, - "platforms": ["mac"] + "platforms": "mac", + "browsers": "chrome" } ``` -## Examples +This context targets Windows or Linux: -### Contexts - -Here are some examples of contexts: +```json +{ + "platforms": ["windows", "linux"], + "browsers": "firefox" +} +``` -- Run tests in Chrome on Windows, macOS, and Linux: +## Examples - ```json - { - "app": { - "name": "chrome" - }, - "platforms": ["windows", "mac", "linux"] - } - ``` +### Simple Contexts -- Run tests in Firefox on Windows and macOS: +- Run tests in Chrome on all supported platforms: ```json { - "app": { - "name": "firefox" - }, - "platforms": ["windows", "mac"] + "platforms": ["windows", "mac", "linux"], + "browsers": "chrome" } ``` -- Run tests in Safari on macOS: +- Run tests in Firefox on Windows and macOS: ```json { - "app": { - "name": "safari" - }, - "platforms": ["mac"] + "platforms": ["windows", "mac"], + "browsers": "firefox" } ``` -- Run tests in Edge on Windows: +- Run tests in WebKit/Safari on macOS: ```json { - "app": { - "name": "edge" - }, - "platforms": ["windows"] + "platforms": "mac", + "browsers": "webkit" // or "safari" } ``` -### In a config - -You can specify contexts in the `config` object. These contexts apply to all tests in the suite. +### Contexts in a Config (`config.json`) -- Run all tests in each of the apps that are available by default on each platform: +Specify contexts in the top-level `runOn` array. These apply to all tests unless overridden. - ```json - { - "input": ".", - "contexts": [ - { - "app": { - "name": "chrome" - }, - "platforms": ["windows", "mac", "linux"] - }, - { - "app": { - "name": "firefox" - }, - "platforms": ["windows", "mac", "linux"] - }, - { - "app": { - "name": "safari" - }, - "platforms": ["mac"] - }, - { - "app": { - "name": "edge" - }, - "platforms": ["windows"] +```json +{ + "input": ".", + "output": "output", + "runOn": [ + { + "platforms": ["windows", "mac", "linux"], + "browsers": "chrome" + }, + { + "platforms": ["windows", "mac", "linux"], + "browsers": "firefox" + }, + { + "platforms": "mac", + "browsers": { + "name": "webkit", + "window": { "width": 1280, "height": 800 } } - ] - } - ``` - -### In a specification + } + ] +} +``` -You can specify contexts in the `specification` object. These contexts override config-level contexts and apply to all tests in the spec. +### Contexts in a Specification (`*.spec.json`) -This example runs all tests in the spec with Chrome on Windows and macOS: +Specify contexts in the spec's `runOn` array. These override config-level contexts for tests within this spec. ```json { - "contexts": [ + "description": "Specification for login tests", + "runOn": [ { - "app": { - "name": "chrome" - }, - "platforms": ["windows","mac"] + "platforms": ["windows", "mac"], + "browsers": "chrome" } ], "tests": [ - ... + // ... tests in this spec will run on Chrome on Windows & Mac ] } ``` -### In a test - -You can specify contexts in the `test` object. These contexts override config- and spec-level contexts and apply only to that test. +### Contexts in a Test -This example runs a single test in Chrome on Windows: +Specify contexts in the test's `runOn` array. These override config- and spec-level contexts for this specific test. ```json { - "name": "Spec name", + "description": "Main application specification", "tests": [ { - "name": "Test name", - "contexts": [ + "description": "Test login form on Windows/Chrome only", + "runOn": [ { - "app": { - "name": "chrome" - }, - "platforms": ["windows"] + "platforms": "windows", + "browsers": "chrome" } ], "steps": [ - ... + // ... steps for this test ] }, { - ... + "description": "Test dashboard on all default contexts", + // No runOn here, inherits from spec or config + "steps": [ + // ... steps for this test + ] } ] } diff --git a/docs/get-started/create-your-first-test.md b/docs/get-started/create-your-first-test.md index 5ede0fc3..ca25425a 100644 --- a/docs/get-started/create-your-first-test.md +++ b/docs/get-started/create-your-first-test.md @@ -45,27 +45,20 @@ To create your first test, follow these steps: { "steps": [ { - "action": "goTo", "description": "Go to the specified URL", - "url": "https://example.com" + "goTo": "https://example.com" }, { - "action": "find", "description": "Verify the presence of the main heading", - "selector": "h1", - "matchText": "Example Domain" + "find": "Example Domain" }, { - "action": "find", "description": "Verify that the 'More information...' link is present and working", - "selector": "a", - "matchText": "More information...", - "click": true + "click": "More information..." }, { - "action": "saveScreenshot", "description": "Capture a screenshot of the resulting page", - "path": "example.png" + "screenshot": "example.png" } ] } @@ -77,7 +70,8 @@ To create your first test, follow these steps: - [`goTo`](/docs/get-started/actions/goTo.md): Navigates to the specified URL, https://example.com, to start the test flow. - [`find`](/docs/get-started/actions/find.md): Locates elements on the page using CSS selectors such as HTML tags like `h1` or `a`, and validates their presence and text content. - - [`saveScreenshot`](/docs/get-started/actions/saveScreenshot.md): Captures a screenshot of the current page and saves it to the specified path. + - [`click`](/docs/get-started/actions/click.md): Clicks on the specified element, in this case, the `More information...` link. + - [`screenshot`](/docs/get-started/actions/screenshot.md): Captures a screenshot of the current page and saves it to the specified path. 4. Save the file. @@ -93,88 +87,78 @@ To create your first test, follow these steps: After running the test, you should see the results in your terminal, which Doc Detective saves to a new file named `testResults-UNIQUE_ID.json`: -```text title="testResults-UNIQUE_ID.json" +```json title="testResults-UNIQUE_ID.json" { "summary": { "specs": { - "pass": 1, // Number of test specifications that passed - "fail": 0, // Number of test specifications that failed + "pass": 1, // Number of test specifications that passed + "fail": 0, // Number of test specifications that failed "warning": 0, "skipped": 0 }, "tests": { - "pass": 1, // Number of tests that passed - "fail": 0, // Number of tests that failed + "pass": 1, // Number of tests that passed + "fail": 0, // Number of tests that failed "warning": 0, "skipped": 0 }, "contexts": { - "pass": 1, // Number of contexts that passed - "fail": 0, // Number of contexts that failed + "pass": 1, // Number of contexts that passed + "fail": 0, // Number of contexts that failed "warning": 0, "skipped": 0 }, "steps": { - "pass": 4, // Number of individual steps that passed - "fail": 0, // Number of individual steps that failed + "pass": 4, // Number of individual steps that passed + "fail": 0, // Number of individual steps that failed "warning": 0, "skipped": 0 } }, "specs": [ { - "result": "PASS", "tests": [ { "result": "PASS", "contexts": [ { "result": "PASS", - "app": "chrome", - "path": "/opt/homebrew/lib/node_modules/doc-detective/node_modules/doc-detective-core/browser-snapshots/chrome/mac_arm-128.0.6613.119/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing", - "platform": "mac", + "platform": "linux", + "browser": { + "name": "firefox", + "version": "nightly_136.0a1", + "path": "/home/hawkeyexl/Workspaces/doc-detective-core/browser-snapshots/firefox/linux-nightly_136.0a1/firefox/firefox" + }, "steps": [ { "result": "PASS", "resultDescription": "Opened URL.", - "action": "goTo", - "url": "https://example.com", - "id": "89889930-25b1-460b-a57f-68188c7be3d7" + "description": "Go to the specified URL", + "goTo": { + "url": "https://example.com" + }, + "stepId": "b09d9c07-2fb0-4fc9-92a4-b4cabdb5c3cd" }, { "result": "PASS", - "resultDescription": "Found an element matching selector. Matched text.", - "action": "find", + "resultDescription": "Found an element matching selector. Found element by text.", "description": "Verify the presence of the main heading", - "selector": "h1", - "matchText": "Example Domain", - "timeout": 5000, - "moveTo": false, - "click": false, - "setVariables": [], - "id": "f5bc70a7-82cf-4b60-adac-b51a77c1d0c2" + "find": "Example Domain", + "stepId": "94dd9246-e612-43be-829e-607a191550e8" }, { "result": "PASS", - "resultDescription": "Found an element matching selector. Matched text. Clicked element.", - "action": "find", - "description": "Verify that the 'More information...' link is present and is working", - "selector": "a", - "matchText": "More information...", - "click": true, - "timeout": 5000, - "moveTo": false, - "setVariables": [], - "id": "1c9b165b-416f-4c66-b7f5-81e44f5590f4" + "resultDescription": "Clicked element. Found element by text. Clicked element.", + "description": "Verify that the 'More information...' link is present and working", + "click": "More information...", + "stepId": "d5851502-807f-4cb9-981c-338201278f29" }, { "result": "PASS", "resultDescription": "Saved screenshot.", - "action": "saveScreenshot", - "path": "/Users/doc-detective-user/path/to/your/project/example.png", - "maxVariation": 5, - "overwrite": "false", - "id": "c2bb9f2f-f9d7-4e8c-bc53-4a1b557c2a3c" + "description": "Capture a screenshot of the resulting page", + "screenshot": "example.png", + "stepId": "5698d891-e220-4e88-8b96-8c5eb8c1ce5f" } ] } diff --git a/docs/get-started/tests/index.md b/docs/get-started/tests/index.md index c2ad38e7..c15e6f86 100644 --- a/docs/get-started/tests/index.md +++ b/docs/get-started/tests/index.md @@ -11,19 +11,21 @@ Tests tell Doc Detective what actions to perform, how, and where. Tests are made - **Test specification**: The highest-level component, test specifications (or specs) are collection of tests that should run together. [Contexts](/docs/get-started/config/contexts) defined in the spec are shared by all tests in the spec. Test specifications are equivalent to test suites in other testing frameworks. - **Test**: A test to run within a spec. Each test has a name, and a set of steps to perform. Tests are equivalent to test cases in other testing frameworks. -- **Steps**: A step is a single action to perform within a test. Each individual step acts as an assertion that the step completes as expected. Steps are equivalent to assertions in other testing frameworks. +- **Steps**: A step is a single action to perform within a test. Each individual step acts as an assertion that the step completes as expected. Steps are roughly equivalent to assertions in other testing frameworks. - Each step has an action, which is a command that tells Doc Detective what to do. Actions can have additional properties that further define the action. + Each step can perform an action, which is a command that tells Doc Detective what to do. Actions can have additional properties that further define the action. - [**checkLink**](/docs/get-started/actions/checkLink): Check if a URL returns an acceptable status code from a GET request. + - [**click**](/docs/get-started/actions/click): Click or tap an element. - [**find**](/docs/get-started/actions/find): Check if an element exists with the specified selector. - [**goTo**](/docs/get-started/actions/goTo): Navigate to a specified URL. - [**httpRequest**](/docs/get-started/actions/httpRequest): Perform a generic HTTP request, for example to an API. + - [**loadVariables**](/docs/get-started/actions/loadVariables): Load environment variables from a `.env` file. + - [**record**](/docs/get-started/actions/record) and [**stopRecord**](/docs/get-started/actions/stopRecord): Capture a video of test execution. + - [**runCode**](/docs/get-started/actions/runCode): Assemble and run code. - [**runShell**](/docs/get-started/actions/runShell): Perform a native shell command. - - [**saveScreenshot**](/docs/get-started/actions/saveScreenshot): Take a screenshot in PNG format. - - [**setVariables**](/docs/get-started/actions/setVariables): Load environment variables from a `.env` file. - - [**startRecording**](/docs/get-started/actions/startRecording) and [**stopRecording**](/docs/get-started/actions/stopRecording): Capture a video of test execution. - - [**typeKeys**](/docs/get-started/actions/typeKeys): Type keys. To type special keys, begin and end the string with `$` and use the special key’s enum. For example, to type the Escape key, enter `$ESCAPE$`. + - [**screenshot**](/docs/get-started/actions/screenshot): Take a screenshot in PNG format. + - [**type**](/docs/get-started/actions/type): Type keys. To type special keys, begin and end the string with `$` and use the special key’s enum. For example, to type the Escape key, enter `$ESCAPE$`. - [**wait**](/docs/get-started/actions/wait): Pause before performing the next action. ## Define a test @@ -40,18 +42,17 @@ Test specs in standalone JSON files use the following basic structure: ```json { - "id": "spec-id", + "specId": "spec-id", "description": "Spec Description", "tests": [ { - "id": "test-id", + "testId": "test-id", "description": "Test Description", "steps": [ { - "id": "step-id", - "description": "Step Description", - "action": "action-name" - // Additional step properties + "stepId": "step-id", + "description": "Step Description" + // Additional step properties, such as find, goTo, httpRequest, etc. } ] } @@ -69,25 +70,22 @@ Here's an example test for performing a Google search and saving a screenshot of { "steps": [ { - "action": "goTo", - "url": "https://www.google.com" + "goTo": "https://www.google.com" }, { - "action": "find", - "selector": "[title=Search]", - "click": true + "find": { + "selector": "[title=Search]", + "click": true + } }, { - "action": "typeKeys", - "keys": ["American Shorthair kittens", "$ENTER$"] + "type": ["American Shorthair kittens", "$ENTER$"] }, { - "action": "wait", - "duration": 5000 + "wait": 5000 }, { - "action": "saveScreenshot", - "path": "search-results.png" + "screenshot": "search-results.png" } ] } @@ -118,23 +116,23 @@ If you declare a step without declaring a test, Doc Detective automatically crea Here's an example of an inline test for performing a Google search and saving a screenshot of the results: ```markdown -[comment]: # 'test {"id": "kitten-search"}' +[comment]: # 'test {"testId": "kitten-search"}' To search for American Shorthair kittens, 1. Go to [Google](https://www.google.com). - [comment]: # 'step {"action":"goTo", "url":"https://www.google.com"}' + [comment]: # 'step {"goTo": "https://www.google.com"}' 2. In the search bar, enter "American Shorthair kittens", then press Enter. - [comment]: # 'step { "action": "find", "selector": "[title=Search]", "click": true }' - [comment]: # 'step { "action": "typeKeys", "keys": ["American Shorthair kittens", "$ENTER$"] }' - [comment]: # 'step { "action": "wait", "duration": 5000 }' + [comment]: # 'step { "find": { "selector": "[title=Search]", "click": true } }' + [comment]: # 'step { "type": ["American Shorthair kittens", "$ENTER$"] }' + [comment]: # 'step { "wait": 5000 }' ![Search results](search-results.png) -[comment]: # 'step { "action": "saveScreenshot", "path": "search-results.png" }' +[comment]: # 'step { "screenshot": "search-results.png" }' [comment]: # "test end" ``` @@ -151,11 +149,10 @@ For example, markup for Markdown files might look like this: ```json { ... - "runTests": { - "detectSteps": true - }, "fileTypes": [ { + "name": "Markdown", + "extensions": ["md", "markdown", "mdx"], ... "markup": [ { @@ -179,10 +176,10 @@ For example, markup for Markdown files might look like this: "name": "Click", "regex": ["(?:[Cc]lick|[Pp]ress|[Cc]hoose|[Tt]ap)\\s+\\*\\*(.+?)\\*\\*"], "actions": [{ - "action": "find", - "description": "Click $1", - "selector": "aria/$1", - "click": true + "find": { + "selector": "aria/$1", + "click": true + } }] }, { @@ -190,11 +187,12 @@ For example, markup for Markdown files might look like this: "regex": ["!\\[.+?\\]\\((.+?)\\)"], "actions": [ { - "action": "saveScreenshot", - "path": "$1", - "directory": "samples", - "maxVariation": 5, - "overwrite": "byVariation" + "screenshot": { + "path": "$1", + "directory": "samples", + "maxVariation": 5, + "overwrite": "byVariation" + } } ] } @@ -216,9 +214,9 @@ Markdown: To get started, 1. Go to [Acme Console](https://console.acme.com). -2. Press **Search**. +2. Click **Search**. -![Search results](search-results.png) +![Search results](search-results.png){ .screenshot } ``` @@ -231,17 +229,13 @@ Detected tests: { "steps": [ { - "action": "goTo", - "url": "https://console.acme.com" + "goTo": "https://console.acme.com" }, { - "action": "find", - "selector": "aria/Search", - "click": true + "click": "Search" }, { - "action": "saveScreenshot", - "path": "search-results.png" + "screenshot": "search-results.png" } ] } @@ -260,10 +254,15 @@ The default actions are as follows: Check that the match returns a valid status code. ```json -{ - "action": "checkLink", - "url": "$1" -} +{ "checkLink": "$1" } +``` + +### `click` + +Click or tap the match. + +```json +{ "click": "$1" } ``` #### `goTo` @@ -271,10 +270,7 @@ Check that the match returns a valid status code. Open the match as a URL in a browser. ```json -{ - "action": "goTo", - "url": "$1" -} +{ "goTo": "$1" } ``` #### `find` @@ -282,32 +278,23 @@ Open the match as a URL in a browser. Find an element on the current page that has an ARIA label that equals the match. ```json -{ - "action": "find", - "selector": "aria/$1" -} +{ "find": "$1" } ``` -#### `saveScreenshot` +#### `screenshot` Save a screenshot to a path equalling the match. ```json -{ - "action": "saveScreenshot", - "path": "$1" -} +{ "screenshot": "$1" } ``` -#### `typeKeys` +#### `type` Type the match into the current page. ```json -{ - "action": "typeKeys", - "keys": "$1" -} +{ "type": "$1" } ``` #### `httpRequest` @@ -315,10 +302,7 @@ Type the match into the current page. Make an GET request to the match. ```json -{ - "action": "httpRequest", - "url": "$1" -} +{ "httpRequest": "$1" } ``` #### `runShell` @@ -326,31 +310,23 @@ Make an GET request to the match. Run the match as a shell command. ```json -{ - "action": "runShell", - "command": "$1" -} +{ "runShell": "$1" } ``` -#### `startRecording` +#### `record` Start recording a video to a path equalling the match. ```json -{ - "action": "startRecording", - "path": "$1" -} +{ "record": "$1" } ``` -#### `stopRecording` +#### `stopRecord` Stop recording a video. ```json -{ - "action": "stopRecording" -} +{ "stopRecord": true } ``` #### `wait` @@ -358,37 +334,31 @@ Stop recording a video. Wait for the specified duration. ```json -{ - "action": "wait", - "duration": "$1" -} +{ "wait": "$1" } ``` -#### `setVariables` +#### `loadVariables` Load environment variables from an `.env` file, where the path is the match. ```json -{ - "action": "setVariables", - "path": "$1" -} +{ "loadVariables": "$1" } ``` ## Run tests -Doc Detective's `runTest` command runs your tests. Input files are read from your config's `input` and `runTests.input` properties, but you can also specify input files directly in the command with the `--input` flag. +Doc Detective's `doc-detective` command runs your tests. Input files are read from your config's `input` property, but you can also specify input files directly in the command with the `--input` flag. This example runs all test specs in your config's `input` and `runTest.input` parameters: ```bash -npx doc-detective runTests +npx doc-detective ``` This example runs all test specs in a file named `doc-content.md` in the `samples` directory: ```bash -npx doc-detective runTests --input ./samples/doc-content.md +npx doc-detective --input ./samples/doc-content.md ``` ### Remotely hosted tests @@ -396,15 +366,15 @@ npx doc-detective runTests --input ./samples/doc-content.md You can run tests hosted remotely by specifying the URL of the test file with the `--input` argument. For example, to run tests from a file hosted at `https://doc-detective.com/sample.spec.json`, run the following command: ```bash -npx doc-detective runTests --input https://doc-detective.com/sample.spec.json +npx doc-detective --input https://doc-detective.com/sample.spec.json ``` These tests run the same way as local tests, but Doc Detective fetches the test file from the specified URL and stores it in a temporary directory. The URL must be accessible to the machine running the tests. ## Read the results -Doc Detective outputs test results to a `testResults-.json` file in your `output` or `runTests.output` directory. You can also specify your output directory with the `--output` flag: +Doc Detective outputs test results to a `testResults-.json` file in your `output` directory. You can also specify your output directory with the `--output` flag: ```bash -npx doc-detective runTests --input ./samples/doc-content.md --output ./samples/ +npx doc-detective --input ./samples/doc-content.md --output ./samples/ ``` diff --git a/docs/references/schemas/checkLink.md b/docs/references/schemas/checkLink.md index 4e774f3a..3a5460f1 100644 --- a/docs/references/schemas/checkLink.md +++ b/docs/references/schemas/checkLink.md @@ -1,41 +1,35 @@ # checkLink -Check if a URL returns an acceptable status code from a GET request. + ## Fields Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. Action to perform. | -url | string | Required. URL to check. | +url | string | Optional. URL to check. Can be a full URL or a path. If a path is provided, `origin` must be specified. | origin | string | Optional. Protocol and domain to navigate to. Prepended to `url`. | -statusCodes | array of integers | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. | ``[200,201,202]`` +statusCodes | One of
- integer
- array of integer | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. | ``[200,301,302,307,308]`` ## Examples ```json -{ - "action": "checkLink", - "url": "https://www.google.com" -} +"https://www.google.com" +``` + +```json +"/search" ``` ```json { - "action": "checkLink", "url": "https://www.google.com", - "statusCodes": [ - 200 - ] + "statusCodes": 200 } ``` ```json { - "action": "checkLink", "url": "/search", "origin": "www.google.com", "statusCodes": [ diff --git a/docs/references/schemas/click.md b/docs/references/schemas/click.md new file mode 100644 index 00000000..dce20c57 --- /dev/null +++ b/docs/references/schemas/click.md @@ -0,0 +1,37 @@ + +# click + +Click or tap an element. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- +button | string | Optional. Kind of click to perform.

Accepted values: `left`, `right`, `middle` | +elementText | string | Optional. Display text of the element to click. If combined with `selector`, the element must match both the text and the selector. | +selector | string | Optional. Selector of the element to click. If combined with `elementText`, the element must match both the text and the selector. | + +## Examples + +```json +true +``` + +```json +"right" +``` + +```json +{ + "button": "left", + "elementText": "Element text" +} +``` + +```json +{ + "selector": "#elementToScreenshot", + "elementText": "Element text", + "button": "middle" +} +``` diff --git a/docs/references/schemas/config.md b/docs/references/schemas/config.md index abeaa424..6056f23e 100644 --- a/docs/references/schemas/config.md +++ b/docs/references/schemas/config.md @@ -7,53 +7,24 @@ Configuration options for Doc Detective operations. Field | Type | Description | Default :-- | :-- | :-- | :-- -defaultCommand | string | Optional. Default command to run when no command is specified.

Accepted values: `runTests`, `runCoverage` | -input | One of
- string
- array of strings | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. | `.` -output | string | Optional. Path of the of the file or directory in which to store the output of Doc Detective commands. If a file path is specified, the output is written to that file. If a file of that name already exists, Doc Detective creates appends an integer to the result file name. If a directory path is specified, the output file name is dependent on the command being run. | `.` -recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specificaions and source files. | `true` -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`).

Accepted values: `cwd`, `file` | `cwd` -envVariables | string | Optional. Path to a `.env` file to load before performing a Doc Detective operation. | -runTests | object | Optional. Options for running tests. When running tests, values set here override general configuration options. | -runTests.input | One of
- string
- array of strings | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. | -runTests.output | string | Optional. Path of the of the file or directory in which to store the output of Doc Detective commands. If a file path is specified, the output is written to that file. If a file of that name already exists, Doc Detective creates appends an integer to the result file name. If a directory path is specified, the output file name is dependent on the command being run. | `.` -runTests.setup | One of
- string
- array of strings | Optional. Path(s) to test specifications to perform before those specified by `input`. Useful for setting up testing environments. | -runTests.cleanup | One of
- string
- array of strings | Optional. Path(s) to test specifications to perform after those specified by `input`. Useful for cleaning up testing environments. | -runTests.recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specificaions and source files. | -runTests.detectSteps | boolean | Optional. Whether or not to detect steps in input files based on markup regex. | `false` -runTests.mediaDirectory | string | Optional. DEPRECATED. | `.` -runTests.downloadDirectory | string | Optional. Path of the directory in which to store downloaded files. | `.` -runTests.contexts | array of object([context](/docs/references/schemas/context)) | Optional. Application/platform sets to run tests in. If no contexts are specified but a context is required by one or more tests, Doc Detective attempts to identify a supported context in the current environment and run tests against it. | ``[{"app":{"name":"firefox","options":{"width":1200,"height":800,"headless":true}},"platforms":["linux","mac","windows"]}]`` -runCoverage | object | Optional. Options for performing test coverage analysis on documentation source files. When performing coveration analysis, values set here override general configuration options. | -runCoverage.input | One of
- string
- array of strings | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. | -runCoverage.output | string | Optional. Path of the of the file or directory in which to store the output of Doc Detective commands. If a file path is specified, the output is written to that file. If a file of that name already exists, Doc Detective creates appends an integer to the result file name. If a directory path is specified, the output file name is dependent on the command being run. | `.` -runCoverage.recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specificaions and source files. | -runCoverage.markup | array of strings | Optional. Markup types to include when performing this operation. If no markup types are specified, the operation includes all markup types as defined in `fileTypes`. | ``["onscreenText","emphasis","image","hyperlink","codeInline","codeBlock","interaction"]`` -suggestTests | object | Optional. Options for suggesting tests based on documentation source files. When suggesting tests, values set here override general condiguration options. | -suggestTests.input | One of
- string
- array of strings | Optional. Path(s) to test specifications and documentation source files. May be paths to specific files or to directories to scan for files. | -suggestTests.output | string | Optional. Path of the of the file or directory in which to store the output of Doc Detective commands. If a file path is specified, the output is written to that file. If a file of that name already exists, Doc Detective creates appends an integer to the result file name. If a directory path is specified, the output file name is dependent on the command being run. | `.` -suggestTests.recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specificaions and source files. | -suggestTests.markup | array of strings | Optional. Markup types to include when performing this operation. If no markup types are specified, the operation includes all markup types as defined in `fileTypes`. | ``["onscreenText","emphasis","image","hyperlink","codeInline","codeBlock","interaction"]`` -fileTypes | array of objects | Optional. Information on supported file types and how to parse the markup within them. | [] -fileTypes.name | string | Optional. Name of the file type. | -fileTypes.extensions | array of strings | Required. File extensions to support with this configuration. | -fileTypes.testStartStatementOpen | string | Required. Opening of an in-document test start statement. | -fileTypes.testStartStatementClose | string | Required. Close of an in-document test start statement. | -fileTypes.testIgnoreStatement | string | Required. Text for an in-document test ignore statement. | -fileTypes.testEndStatement | string | Required. Text for an in-document test end statement. | -fileTypes.stepStatementOpen | string | Required. Opening of an in-document step statement. | -fileTypes.stepStatementClose | string | Required. Close of an in-document step statement. | -fileTypes.markup | array of objects | Required. Markup types and associated regex patterns to find in documentation source files. | -fileTypes.markup.name | string | Required. Name of the markup type. | -fileTypes.markup.regex | array of strings | Required. Regex patterns to find the markup type in documentation source files. | -fileTypes.markup.actions | array of
- strings
- objects
- object([checkLink](/docs/references/schemas/checkLink))
- object([find](/docs/references/schemas/find))
- object([goTo](/docs/references/schemas/goTo))
- object([httpRequest](/docs/references/schemas/httpRequest))
- object([runShell](/docs/references/schemas/runShell))
- object([saveScreenshot](/docs/references/schemas/saveScreenshot))
- object([setVariables](/docs/references/schemas/setVariables))
- object([startRecording](/docs/references/schemas/startRecording))
- object([stopRecording](/docs/references/schemas/stopRecording))
- object([typeKeys](/docs/references/schemas/typeKeys))
- object([wait](/docs/references/schemas/wait)) | Optional. Actions that apply to the markup type. | -fileTypes.markup.actions.name | string | Required. Name of the action.

Accepted values: `checkLink`, `find`, `goTo`, `httpRequest`, `runShell`, `saveScreenshot`, `setVariables`, `startRecording`, `stopRecording`, `typeKeys`, `wait` | -fileTypes.markup.actions.params | object | Optional. Parameters for the action. | +configId | string | Optional. Identifier for the configuration. | Generated UUID +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. | `.` +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. | `.` +recursive | boolean | Optional. If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specifications and source files. | `true` +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`).

Accepted values: `cwd`, `file` | `file` +loadVariables | string | Optional. Load environment variables from the specified `.env` file. | +origin | string | Optional. Default protocol and domain to use for relative URLs. | +beforeAny | One of
- string
- array of string | Optional. Path(s) to test specifications to perform before those specified by `input`. Useful for setting up testing environments. | +afterAll | One of
- string
- array of string | Optional. Path(s) to test specifications to perform after those specified by `input`. Useful for cleaning up testing environments. | +detectSteps | boolean | Optional. Whether or not to detect steps in input files based on defined markup. | `true` +logLevel | string | Optional. Amount of detail to output when performing an operation.

Accepted values: `silent`, `error`, `warning`, `info`, `debug` | `info` +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. | +fileTypes | array of
one of:
- string
- object | Optional. Configuration for file types and their markup detection. | [] integrations | object | Optional. Options for connecting to external services. | -integrations.openApi | array of undefineds | Optional. undefined | +integrations.openApi | array of unknown | Optional. No description provided. | telemetry | object | Optional. Options around sending telemetry for Doc Detective usage. | ``{"send":true}`` telemetry.send | boolean | Required. If `true`, sends Doc Detective telemetry. | `true` telemetry.userId | string | Optional. Identifier for the organization, group, or individual running Doc Detective. | -logLevel | string | Optional. Amount of detail to output when performing an operation.

Accepted values: `silent`, `error`, `warning`, `info`, `debug` | `info` ## Examples @@ -63,451 +34,77 @@ logLevel | string | Optional. Amount of detail to output when performing an ope ```json { - "input": ".", - "output": "." -} -``` - -```json -{ - "defaultCommand": "runTests", - "envVariables": "", "input": ".", "output": ".", "recursive": true, - "logLevel": "info", - "runTests": { - "input": ".", - "output": ".", - "setup": "", - "cleanup": "", - "recursive": true, - "downloadDirectory": ".", - "contexts": [ - { - "app": { - "name": "firefox", - "path": "" - }, - "platforms": [ - "linux", - "mac", - "windows" - ] - } - ] - } -} -``` - -```json -{ - "integrations": { - "openApi": [ - { - "name": "Acme", - "descriptionPath": "https://www.acme.com/openapi.json", - "server": "https://api.acme.com", - "mockResponse": true - } - ] - } + "loadVariables": ".env", + "fileTypes": [ + "markdown" + ] } ``` ```json { - "envVariables": "", - "input": ".", - "output": ".", - "recursive": true, - "logLevel": "info", - "runTests": { - "input": ".", - "output": ".", - "setup": "", - "cleanup": "", - "recursive": true, - "downloadDirectory": ".", - "contexts": [ - { - "app": { - "name": "firefox", - "path": "" - }, - "platforms": [ - "linux", - "mac", - "windows" - ] - } - ] - }, - "runCoverage": { - "recursive": true, - "input": ".", - "output": ".", - "markup": [] - }, "fileTypes": [ { - "name": "Markdown", + "extends": "markdown", "extensions": [ - ".md", - ".markdown", - ".mdx" + "md", + "markdown", + "mdx" ], - "testStartStatementOpen": "[comment]: # (test start", - "testStartStatementClose": ")", - "testIgnoreStatement": "[comment]: # (test ignore)", - "testEndStatement": "[comment]: # (test end)", - "stepStatementOpen": "[comment]: # (step", - "stepStatementClose": ")", + "inlineStatements": { + "testStart": "", + "testEnd": "", + "ignoreStart": "", + "ignoreEnd": "", + "step": "" + }, "markup": [ { "name": "onscreenText", - "regex": [ - "\\*\\*.+?\\*\\*" - ], - "actions": [ - "find" - ] - }, - { - "name": "emphasis", - "regex": [ - "(?", - "testIgnoreStatement": "", - "testEndStatement": "", - "stepStatementOpen": "", - "markup": [] + "name": "Python", + "extensions": "py", + "runShell": { + "command": "python $1" + } } - ], - "integrations": {}, - "telemetry": { - "send": true, - "userId": "Doc Detective" - } + ] } ``` diff --git a/docs/references/schemas/context.md b/docs/references/schemas/context.md index 2b6f168c..de93e2d0 100644 --- a/docs/references/schemas/context.md +++ b/docs/references/schemas/context.md @@ -1,92 +1,134 @@ # context -An application and supported platforms. - -If no contexts are specified but a context is required by one or more tests, Doc Detective attempts to identify a supported context in the current environment and run tests against it. For browsers, context priority is Firefox > Chrome > Chromium. +A context in which to perform tests. If no contexts are specified but a context is required by one or more tests, Doc Detective attempts to identify a supported context in the current environment and run tests against it. For example, if a browser isn't specified but is required by steps in the test, Doc Detective will search for and use a supported browser available in the current environment. ## Fields Field | Type | Description | Default :-- | :-- | :-- | :-- -app | object | Required. The application to run. | -app.name | string | Required. Name of the application.

Accepted values: `chrome`, `firefox`, `safari`, `edge` | -app.path | string | Optional. Absolute path or command for the application. If not specified, defaults to typical install paths per platform. If specified but the path is invalid, the context is skipped. | -app.options | object | Optional. Options to pass to the app. Only works when `name` is `firefox` or `chrome`. | -app.options.width | integer | Optional. Width of the window in pixels. | -app.options.height | integer | Optional. Height of the window in pixels. | -app.options.viewport_height | integer | Optional. Height of the viewport in pixels. Overrides `height`. | -app.options.viewport_width | integer | Optional. Width of the viewport in pixels. Overrides `width`. | -app.options.headless | boolean | Optional. If `true`, runs the browser in headless mode. Not supported by Safari. | -app.options.driverPath | string | Optional. Path to the browser driver. If not specified, defaults to internally managed dependencies. | -platforms | array of strings | Required. Supported platforms for the application. | +contextId | string | Optional. Unique identifier for the context. | Generated UUID +platforms | One of
- string
- array of string | Optional. Platforms to run tests on. | +browsers | One of
- string
- object
- array of
  one of:
  - string
  - object | Optional. Browsers to run tests on. | +browsers.name | string | Required. Name of the browser.

Accepted values: `chrome`, `firefox`, `safari`, `webkit` | +browsers.headless | boolean | Optional. If `true`, runs the browser in headless mode. | `true` +browsers.window | object | Optional. Browser dimensions. | +browsers.window.width | integer | Optional. Width of the browser window in pixels. | +browsers.window.height | integer | Optional. Height of the browser window in pixels. | +browsers.viewport | object | Optional. Viewport dimensions. | +browsers.viewport.width | integer | Optional. Width of the viewport in pixels. | +browsers.viewport.height | integer | Optional. Height of the viewport in pixels. | ## Examples ```json { - "app": { - "name": "chrome" - }, + "platforms": "linux", + "browsers": "chrome" +} +``` + +```json +{ "platforms": [ + "windows", + "mac", "linux" + ], + "browsers": [ + "chrome", + "firefox", + "webkit" ] } ``` ```json { - "app": { + "browsers": { "name": "chrome", - "options": { - "viewport_width": 800, - "viewport_height": 600 - } - }, - "platforms": [ - "linux" - ] + "headless": true + } } ``` ```json { - "app": { - "name": "firefox", - "options": { - "width": 800, - "height": 600, - "headless": false, - "driverPath": "/usr/bin/geckodriver" + "browsers": [ + { + "name": "chrome", + "headless": true + }, + { + "name": "firefox" } - }, - "platforms": [ - "linux", - "windows", - "mac" ] } ``` ```json { - "app": { - "name": "safari" - }, "platforms": [ - "mac" - ] + "mac", + "linux" + ], + "browsers": { + "name": "chrome", + "headless": true + } } ``` ```json { - "app": { - "name": "firefox", - "path": "/usr/bin/firefox" - }, "platforms": [ + "windows", + "mac", "linux" + ], + "browsers": [ + { + "name": "chrome", + "headless": true, + "window": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1600, + "height": 900 + } + }, + { + "name": "firefox", + "window": { + "width": 1366, + "height": 768 + } + }, + { + "name": "webkit", + "headless": false, + "viewport": { + "width": 1440, + "height": 900 + } + } + ] +} +``` + +```json +{ + "platforms": "mac", + "browsers": [ + { + "name": "safari", + "window": { + "width": 1280, + "height": 800 + } + } ] } ``` diff --git a/docs/references/schemas/find.md b/docs/references/schemas/find.md index fe739b86..e688be55 100644 --- a/docs/references/schemas/find.md +++ b/docs/references/schemas/find.md @@ -1,49 +1,44 @@ # find -Check if an element exists with the specified CSS selector. +Find an element based on display text or a selector, then optionally interact with it. ## Fields Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. Action to perform. | -selector | string | Required. Selector that uniquely identifies the element. | +elementText | string | Optional. Display text of the element to find. If combined with `selector`, the element must match both the text and the selector. | +selector | string | Optional. Selector of the element to find. If combined with `elementText`, the element must match both the text and the selector. | timeout | integer | Optional. Max duration in milliseconds to wait for the element to exist. | `5000` -matchText | string | Optional. Text that the element should contain. If the element doesn't contain the text, the step fails. Accepts both strings an regular expressions. To use a regular expression, the expression should start and end with a `/`. For example, `/search/`. | -moveTo | [object Object] | Optional. Move to the element. If the element isn't visible, it's scrolled into view. Only runs the if the test is being recorded. | `false` -click | One of
- boolean
- object | Optional. Click the element. | -typeKeys | One of
- string
- object | Optional. Type keys after finding the element. Either a string or an object with a `keys` field as defined in [`typeKeys`](typeKeys). To type in the element, make the element active with the `click` parameter. | -setVariables | array of objects | Optional. Extract environment variables from the element's text. | ``[]`` -setVariables.name | string | Required. Name of the environment variable to set. | -setVariables.regex | string | Required. Regex to extract the environment variable from the element's text. | +moveTo | boolean | Optional. Move to the element. If the element isn't visible, it's scrolled into view. | `true` +click | One of
- object([click](/docs/references/schemas/click))
- object | Optional. Click the element. | +type | undefined | 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. | ## Examples +```json +"Find me!" +``` + ```json { - "action": "find", "selector": "[title=Search]" } ``` ```json { - "action": "find", "selector": "[title=Search]", "timeout": 10000, - "matchText": "Search", + "elementText": "Search", "moveTo": true, "click": true, - "typeKeys": "shorthair cat" + "type": "shorthair cat" } ``` ```json { - "action": "find", "selector": "[title=Search]", "click": { "button": "right" @@ -53,30 +48,16 @@ setVariables.regex | string | Required. Regex to extract the environment variab ```json { - "action": "find", "selector": "[title=Search]", "timeout": 10000, - "matchText": "Search", + "elementText": "Search", "moveTo": true, "click": true, - "typeKeys": { + "type": { "keys": [ "shorthair cat" ], - "delay": 100 + "inputDelay": 100 } } ``` - -```json -{ - "action": "find", - "selector": "[title=ResultsCount]", - "setVariables": [ - { - "name": "resultsCount", - "regex": ".*" - } - ] -} -``` diff --git a/docs/references/schemas/goTo.md b/docs/references/schemas/goTo.md index dfcc2a91..9d8d8dd9 100644 --- a/docs/references/schemas/goTo.md +++ b/docs/references/schemas/goTo.md @@ -1,42 +1,34 @@ # goTo -Navigate to a specified URL. + ## Fields Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. Action to perform. | -url | string | Required. URL to navigate to. | +url | string | Optional. 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. | origin | string | Optional. Protocol and domain to navigate to. Prepended to `url`. | ## Examples ```json -{ - "action": "goTo", - "url": "https://www.google.com" -} +"https://www.google.com" +``` + +```json +"/search" ``` ```json { - "id": "ddec5e20-2e81-4f38-867c-92c8d9516755", - "description": "This is a test!", - "action": "goTo", "url": "https://www.google.com" } ``` ```json { - "id": "ddec5e20-2e81-4f38-867c-92c8d9516756", - "description": "This is a test!", - "action": "goTo", "url": "/search", - "origin": "https://www.google.com" + "origin": "www.google.com" } ``` diff --git a/docs/references/schemas/httpRequest.md b/docs/references/schemas/httpRequest.md index a60a4c90..4f30ced4 100644 --- a/docs/references/schemas/httpRequest.md +++ b/docs/references/schemas/httpRequest.md @@ -7,63 +7,65 @@ Perform a generic HTTP request, for example to an API. Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. Aciton to perform. | url | string | Optional. URL for the HTTP request. | -openApi | undefined | Optional. undefined | -statusCodes | array of integers | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. | ``[200]`` +openApi | unknown | Optional. No description provided. | +statusCodes | array of integer | Optional. Accepted status codes. If the specified URL returns a code other than what is specified here, the action fails. | ``[200,201]`` method | string | Optional. Method of the HTTP request

Accepted values: `get`, `put`, `post`, `patch`, `delete` | `get` timeout | integer | Optional. Timeout for the HTTP request, in milliseconds. | `60000` -requestHeaders | object | Optional. Headers to include in the HTTP request, in key/value format. | ``{}`` -responseHeaders | object | Optional. Headers expected in the response, in key/value format. If one or more `responseHeaders` entries aren't present in the response, the step fails. | ``{}`` -requestParams | object | Optional. URL parameters to include in the HTTP request, in key/value format. | ``{}`` -responseParams | object | Optional. DEPRECATED. | ``{}`` -requestData | object | Optional. JSON object to include as the body of the HTTP request. | ``{}`` -responseData | object | Optional. JSON object expected in the response. If one or more key/value pairs aren't present in the response, the step fails. | ``{}`` -allowAdditionalFields | boolean | Optional. If `false`, the step fails when the response data contains fields not specified in `responseData`. | `true` -savePath | string | Optional. File path to save the command's output, relative to `saveDirectory`. Specify a file extension that matches the expected response type, such as `.json` for JSON content or `.txt` for strings. | -saveDirectory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | -maxVariation | integer | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `savePath`, this value is ignored. | `0` -overwrite | string | Optional. If `true`, overwrites the existing output at `savePath` if it exists. -If `byVariation`, overwrites the existing output at `savePath` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `byVariation` | `false` -envsFromResponseData | array of objects | Optional. Environment variables to set based on response variables, as an object of the environment variable name and the jq filter applied to the response data to identify the variable's value. | ``[]`` -envsFromResponseData.name | string | Required. Name of the environment variable to set. | -envsFromResponseData.jqFilter | string | Required. jq filter to apply to the response data. If the filter doesn't return a value, the environment variable isn't set. | +request | object | Optional. No description provided. | +request.headers | object | Optional. Headers to include in the HTTP request, in key/value format. | ``{}`` +request.parameters | object | Optional. URL parameters to include in the HTTP request, in key/value format. | ``{}`` +request.body | One of
- object
- array of unknown
- string | Optional. JSON object to include as the body of the HTTP request. | ``{}`` +response | object | Optional. No description provided. | +response.headers | object | Optional. Headers expected in the response, in key/value format. If one or more `responseHeaders` entries aren't present in the response, the step fails. | ``{}`` +response.body | One of
- object
- array of unknown
- string | Optional. JSON object expected in the response. If one or more key/value pairs aren't present in the response, the step fails. | ``{}`` +allowAdditionalFields | boolean | Optional. If `false`, the step fails when the response data contains fields not specified in the response body. | `true` +path | string | Optional. File path to save the command's output, relative to `directory`. Specify a file extension that matches the expected response type, such as `.json` for JSON content or `.txt` for strings. | +directory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | +maxVariation | number | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `path`, this value is ignored. | `0` +overwrite | string | Optional. If `true`, overwrites the existing output at `path` if it exists. +If `aboveVariation`, overwrites the existing output at `path` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `aboveVariation` | `aboveVariation` ## Examples +```json +"https://reqres.in/api/users" +``` + ```json { - "action": "httpRequest", "url": "https://reqres.in/api/users" } ``` ```json { - "action": "httpRequest", "url": "https://reqres.in/api/users/2", "method": "put", - "requestData": { - "name": "morpheus", - "job": "zion resident" + "request": { + "body": { + "name": "morpheus", + "job": "zion resident" + } } } ``` ```json { - "action": "httpRequest", "url": "https://reqres.in/api/users", "method": "post", - "requestData": { - "name": "morpheus", - "job": "leader" + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } }, - "responseData": { - "name": "morpheus", - "job": "leader" + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } }, "statusCodes": [ 200, @@ -74,24 +76,27 @@ envsFromResponseData.jqFilter | string | Required. jq filter to apply to the re ```json { - "action": "httpRequest", "url": "https://www.api-server.com", "method": "post", "timeout": 30000, - "requestHeaders": { - "header": "value" - }, - "requestParams": { - "param": "value" - }, - "requestData": { - "field": "value" - }, - "responseHeaders": { - "header": "value" + "request": { + "body": { + "field": "value" + }, + "headers": { + "header": "value" + }, + "parameters": { + "param": "value" + } }, - "responseData": { - "field": "value" + "response": { + "body": { + "field": "value" + }, + "headers": { + "header": "value" + } }, "statusCodes": [ 200 @@ -101,57 +106,67 @@ envsFromResponseData.jqFilter | string | Required. jq filter to apply to the re ```json { - "action": "httpRequest", "url": "https://reqres.in/api/users", "method": "post", - "requestData": { - "name": "morpheus", - "job": "leader" + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } }, - "responseData": { - "name": "morpheus", - "job": "leader" + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } }, "statusCodes": [ 200, 201 ], - "savePath": "response.json", - "saveDirectory": "media", - "maxVariation": 5, - "overwrite": "byVariation" + "path": "response.json", + "directory": "media", + "maxVariation": 0.05, + "overwrite": "aboveVariation" +} +``` + +```json +{ + "openApi": "getUserById" } ``` ```json { - "action": "httpRequest", "openApi": { "name": "Reqres", "operationId": "getUserById" }, - "requestParams": { - "id": 123 + "request": { + "parameters": { + "id": 123 + } } } ``` ```json { - "action": "httpRequest", "openApi": { "descriptionPath": "https://api.example.com/openapi.json", "operationId": "getUserById" }, - "requestParams": { - "id": 123 + "request": { + "parameters": { + "id": 123 + } } } ``` ```json { - "action": "httpRequest", "openApi": { "descriptionPath": "https://api.example.com/openapi.json", "operationId": "createUser", @@ -162,7 +177,6 @@ envsFromResponseData.jqFilter | string | Required. jq filter to apply to the re ```json { - "action": "httpRequest", "openApi": { "descriptionPath": "https://api.example.com/openapi.json", "operationId": "updateUser", @@ -174,13 +188,12 @@ envsFromResponseData.jqFilter | string | Required. jq filter to apply to the re ```json { - "action": "httpRequest", "openApi": { "descriptionPath": "https://api.example.com/openapi.json", "operationId": "updateUser", "useExample": "request", "exampleKey": "acme", - "requestHeaders": { + "headers": { "Authorization": "Bearer $TOKEN" } } diff --git a/docs/references/schemas/loadVariables.md b/docs/references/schemas/loadVariables.md new file mode 100644 index 00000000..3d18c833 --- /dev/null +++ b/docs/references/schemas/loadVariables.md @@ -0,0 +1,15 @@ + +# loadVariables + +Load environment variables from the specified `.env` file. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- + +## Examples + +```json +".env" +``` diff --git a/docs/references/schemas/record.md b/docs/references/schemas/record.md new file mode 100644 index 00000000..e3d6a0d2 --- /dev/null +++ b/docs/references/schemas/record.md @@ -0,0 +1,30 @@ + +# record + +Start recording the current browser viewport. Must be followed by a `stopRecord` step. Only runs in Chrome browsers when they are visible. Supported extensions: [ '.mp4', '.webm', '.gif' ] + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- +path | string | Optional. File path of the recording. Supports the `.mp4`, `.webm`, and `.gif` extensions. If not specified, the file name is the ID of the step, and the extension is `.mp4`. | +directory | string | Optional. Directory of the file. If the directory doesn't exist, creates the directory. | +overwrite | string | Optional. If `true`, overwrites the existing recording at `path` if it exists.

Accepted values: `true`, `false` | + +## Examples + +```json +true +``` + +```json +"results.mp4" +``` + +```json +{ + "path": "results.mp4", + "directory": "static/media", + "overwrite": "true" +} +``` diff --git a/docs/references/schemas/runCode.md b/docs/references/schemas/runCode.md index 55c7fe23..19703274 100644 --- a/docs/references/schemas/runCode.md +++ b/docs/references/schemas/runCode.md @@ -7,34 +7,23 @@ Assemble and run code. Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -language | string | Required. Language of the code to run. If not specified, the code is run in the shell.

Accepted values: `python`, `bash`, `javascript` | -code | string | Required. Code to run. | -args | array of strings | Optional. Arguments for the command. | ``[]`` +language | string | Optional. Language of the code to run.

Accepted values: `python`, `bash`, `javascript` | +code | string | Optional. Code to run. | +args | array of string | Optional. Arguments for the command. | ``[]`` workingDirectory | string | Optional. Working directory for the command. | `.` -exitCodes | array of integers | Optional. Expected exit codes of the command. If the command's actual exit code isn't in this list, the step fails. | ``[0]`` -output | string | Optional. Content expected in the command's output. If the expected content can't be found in the command's output (either stdout or stderr), the step fails. Supports strings and regular expressions. To use a regular expression, the string must start and end with a forward slash, like in `/^hello-world.*/`. | -savePath | string | Optional. File path to save the command's output, relative to `saveDirectory`. | -saveDirectory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | -maxVariation | integer | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `savePath`, this value is ignored. | `0` -overwrite | string | Optional. If `true`, overwrites the existing output at `savePath` if it exists. -If `byVariation`, overwrites the existing output at `savePath` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `byVariation` | `false` +exitCodes | array of integer | Optional. Expected exit codes of the command. If the command's actual exit code isn't in this list, the step fails. | ``[0]`` +stdio | string | Optional. Content expected in the command's output. If the expected content can't be found in the command's output (either stdout or stderr), the step fails. Supports strings and regular expressions. To use a regular expression, the string must start and end with a forward slash, like in `/^hello-world.*/`. | +path | string | Optional. File path to save the command's output, relative to `directory`. | +directory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | +maxVariation | number | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `path`, this value is ignored. | `0` +overwrite | string | Optional. If `true`, overwrites the existing output at `path` if it exists. +If `aboveVariation`, overwrites the existing output at `path` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `aboveVariation` | `aboveVariation` timeout | integer | Optional. Max time in milliseconds the command is allowed to run. If the command runs longer than this, the step fails. | `60000` -setVariables | array of objects | Optional. Extract environment variables from the command's output. | ``[]`` -setVariables.name | string | Required. Name of the environment variable to set. | -setVariables.regex | string | Required. Regex to extract the environment variable from the command's output. | -outputs | object | Optional. Outputs from step processes and user-defined expressions. Use the `outputs` object to reference outputs in subsequent steps. If a user-defined output matches the key for a step-defined output, the user-defined output takes precedence. | -outputs.stdout | string | Optional. Standard output of the command. | -outputs.stderr | string | Optional. Standard error of the command. | -outputs.exitCode | integer | Optional. Exit code of the command. | ## Examples ```json { - "action": "runCode", "language": "javascript", "code": "console.log('Hello, ${process.env.USER}!');" } @@ -42,20 +31,18 @@ outputs.exitCode | integer | Optional. Exit code of the command. | ```json { - "action": "runCode", "language": "bash", "code": "docker run hello-world", "timeout": 20000, "exitCodes": [ 0 ], - "output": "Hello from Docker!" + "stdio": "Hello from Docker!" } ``` ```json { - "action": "runCode", "language": "javascript", "code": "return false", "exitCodes": [ @@ -66,17 +53,16 @@ outputs.exitCode | integer | Optional. Exit code of the command. | ```json { - "action": "runCode", "language": "python", "code": "print('Hello from Python')", "workingDirectory": ".", "exitCodes": [ 0 ], - "output": "Hello from Python!", - "savePath": "python-output.txt", - "saveDirectory": "output", - "maxVariation": 10, - "overwrite": "byVariation" + "stdio": "Hello from Python!", + "path": "python-output.txt", + "directory": "output", + "maxVariation": 0.1, + "overwrite": "aboveVariation" } ``` diff --git a/docs/references/schemas/runShell.md b/docs/references/schemas/runShell.md index a99d9211..7fe51191 100644 --- a/docs/references/schemas/runShell.md +++ b/docs/references/schemas/runShell.md @@ -7,29 +7,26 @@ Perform a native shell command. Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -command | string | Required. Command to perform in the machine's default shell. | -args | array of strings | Optional. Arguments for the command. | ``[]`` +command | string | Optional. Command to perform in the machine's default shell. | +args | array of string | Optional. Arguments for the command. | ``[]`` workingDirectory | string | Optional. Working directory for the command. | `.` -exitCodes | array of integers | Optional. Expected exit codes of the command. If the command's actual exit code isn't in this list, the step fails. | ``[0]`` -output | string | Optional. Content expected in the command's output. If the expected content can't be found in the command's output (either stdout or stderr), the step fails. Supports strings and regular expressions. To use a regular expression, the string must start and end with a forward slash, like in `/^hello-world.*/`. | -savePath | string | Optional. File path to save the command's output, relative to `saveDirectory`. | -saveDirectory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | -maxVariation | integer | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `savePath`, this value is ignored. | `0` -overwrite | string | Optional. If `true`, overwrites the existing output at `savePath` if it exists. -If `byVariation`, overwrites the existing output at `savePath` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `byVariation` | `false` +exitCodes | array of integer | Optional. Expected exit codes of the command. If the command's actual exit code isn't in this list, the step fails. | ``[0]`` +stdio | string | Optional. Content expected in the command's stdout or stderr. If the expected content can't be found in the command's stdout or stderr, the step fails. Supports strings and regular expressions. To use a regular expression, the string must start and end with a forward slash, like in `/^hello-world.*/`. | +path | string | Optional. File path to save the command's output, relative to `directory`. | +directory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | +maxVariation | number | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `path`, this value is ignored. | `0` +overwrite | string | Optional. If `true`, overwrites the existing output at `path` if it exists. +If `aboveVariation`, overwrites the existing output at `path` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `aboveVariation` | `aboveVariation` timeout | integer | Optional. Max time in milliseconds the command is allowed to run. If the command runs longer than this, the step fails. | `60000` -setVariables | array of objects | Optional. Extract environment variables from the command's output. | ``[]`` -setVariables.name | string | Required. Name of the environment variable to set. | -setVariables.regex | string | Required. Regex to extract the environment variable from the command's output. | ## Examples +```json +"docker run hello-world" +``` + ```json { - "action": "runShell", "command": "echo", "args": [ "$USER" @@ -39,31 +36,26 @@ setVariables.regex | string | Required. Regex to extract the environment variab ```json { - "action": "runShell", "command": "echo", "args": [ "hello-world" - ], - "id": "ddec5e20-2e81-4f38-867c-92c8d9516755", - "description": "This is a test!" + ] } ``` ```json { - "action": "runShell", "command": "docker run hello-world", "timeout": 20000, "exitCodes": [ 0 ], - "output": "Hello from Docker!" + "stdio": "Hello from Docker!" } ``` ```json { - "action": "runShell", "command": "false", "exitCodes": [ 1 @@ -73,7 +65,6 @@ setVariables.regex | string | Required. Regex to extract the environment variab ```json { - "action": "runShell", "command": "echo", "args": [ "setup" @@ -81,28 +72,21 @@ setVariables.regex | string | Required. Regex to extract the environment variab "exitCodes": [ 0 ], - "output": "/.*?/", - "setVariables": [ - { - "name": "TEST", - "regex": ".*" - } - ] + "stdio": "/.*?/" } ``` ```json { - "action": "runShell", "command": "docker run hello-world", "workingDirectory": ".", "exitCodes": [ 0 ], - "output": "Hello from Docker!", - "savePath": "docker-output.txt", - "saveDirectory": "output", - "maxVariation": 10, - "overwrite": "byVariation" + "stdio": "Hello from Docker!", + "path": "docker-output.txt", + "directory": "output", + "maxVariation": 0.1, + "overwrite": "aboveVariation" } ``` diff --git a/docs/references/schemas/saveScreenshot.md b/docs/references/schemas/saveScreenshot.md deleted file mode 100644 index b97d66f2..00000000 --- a/docs/references/schemas/saveScreenshot.md +++ /dev/null @@ -1,93 +0,0 @@ - -# saveScreenshot - -Takes a screenshot in PNG format. - -## Fields - -Field | Type | Description | Default -:-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -path | string | Optional. File path of the PNG file, relative to `directory`. If not specified, the file name is the ID of the step. | -directory | string | Optional. Directory of the PNG file. If the directory doesn't exist, creates the directory. | -maxVariation | number | Optional. Allowed variation in percentage of pixels between the new screenshot and the exisitng screenshot at `path`. If the difference between the new screenshot and the existing screenshot is greater than `maxVariation`, the step fails. If a screenshot doesn't exist at `path`, this value is ignored. | `5` -overwrite | string | Optional. If `true`, overwrites the existing screenshot at `path` if it exists. -If `byVariation`, overwrites the existing screenshot at `path` if the difference between the new screenshot and the existing screenshot is greater than `maxVariation`.

Accepted values: `true`, `false`, `byVariation` | `false` -crop | object | Optional. Crops the screenshot. | -crop.selector | string | Required. Selector of the element to crop the image to. | -crop.padding | One of
- number
- object | Optional. undefined | - -## Examples - -```json -{ - "action": "saveScreenshot" -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png" -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png", - "directory": "static/images" -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png", - "directory": "static/images", - "maxVariation": 10, - "overwrite": "byVariation" -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png", - "directory": "static/images", - "crop": { - "selector": "#element" - } -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png", - "directory": "static/images", - "crop": { - "selector": "#element", - "padding": 10 - } -} -``` - -```json -{ - "action": "saveScreenshot", - "path": "results.png", - "directory": "static/images", - "crop": { - "selector": "#element", - "padding": { - "top": 10, - "right": 20, - "bottom": 30, - "left": 40 - } - } -} -``` diff --git a/docs/references/schemas/screenshot.md b/docs/references/schemas/screenshot.md new file mode 100644 index 00000000..fcdae4f3 --- /dev/null +++ b/docs/references/schemas/screenshot.md @@ -0,0 +1,71 @@ + +# screenshot + +Takes a screenshot in PNG format. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- +path | string | Optional. File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. | +directory | string | Optional. Directory of the PNG file. If the directory doesn't exist, creates the directory. | +maxVariation | number | Optional. Allowed variation in percentage of pixels between the new screenshot and the existing screenshot at `path`. If the difference between the new screenshot and the existing screenshot is greater than `maxVariation`, the step fails. If a screenshot doesn't exist at `path`, this value is ignored. | `0.05` +overwrite | string | Optional. If `true`, overwrites the existing screenshot at `path` if it exists. +If `aboveVariation`, overwrites the existing screenshot at `path` if the difference between the new screenshot and the existing screenshot is greater than `maxVariation`.

Accepted values: `true`, `false`, `aboveVariation` | `aboveVariation` +crop | One of
- string([Crop by element](/docs/references/schemas/Crop by element))
- object([Crop by element (detailed)](/docs/references/schemas/Crop by element (detailed))) | Optional. No description provided. | + +## Examples + +```json +true +``` + +```json +"image.png" +``` + +```json +"static/images/image.png" +``` + +```json +"/User/manny/projects/doc-detective/static/images/image.png" +``` + +```json +{ + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation", + "crop": "#elementToScreenshot" +} +``` + +```json +{ + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation" +} +``` + +```json +{ + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation", + "crop": { + "selector": "#elementToScreenshot", + "elementText": "Element text", + "padding": { + "top": 0, + "right": 0, + "bottom": 0, + "left": 0 + } + } +} +``` diff --git a/docs/references/schemas/setVariables.md b/docs/references/schemas/setVariables.md deleted file mode 100644 index 1397720c..00000000 --- a/docs/references/schemas/setVariables.md +++ /dev/null @@ -1,22 +0,0 @@ - -# setVariables - -Load environment variables from a `.env` file. - -## Fields - -Field | Type | Description | Default -:-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. Action to perform. | -path | string | Required. Path to the `.env` file. | - -## Examples - -```json -{ - "action": "setVariables", - "path": ".env" -} -``` diff --git a/docs/references/schemas/specification.md b/docs/references/schemas/specification.md index 5bd419be..7afd8d5e 100644 --- a/docs/references/schemas/specification.md +++ b/docs/references/schemas/specification.md @@ -7,11 +7,11 @@ Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. Unique identifier for the test specification. | +specId | string | Optional. Unique identifier for the test specification. | Generated UUID description | string | Optional. Description of the test specification. | -file | string | Optional. Path to the file that the specification is associated with. | -contexts | array of object([context](/docs/references/schemas/context)) | Optional. Application/platform sets to run tests in. Overrides `contexts` defined at the config-level. | -openApi | array of undefineds | Optional. undefined | +contentPath | string | Optional. Path to the content that the specification is associated with. | +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. | +openApi | array of unknown | Optional. No description provided. | tests | array of object([test](/docs/references/schemas/test)) | Required. [Tests](test) to perform. | ## Examples @@ -22,8 +22,9 @@ tests | array of object([test](/docs/references/schemas/test)) | Required. [Tes { "steps": [ { - "action": "checkLink", - "url": "https://www.duckduckgo.com" + "checkLink": { + "url": "https://www.duckduckgo.com" + } } ] } @@ -33,94 +34,110 @@ tests | array of object([test](/docs/references/schemas/test)) | Required. [Tes ```json { - "id": "Do all the things! - Spec", - "contexts": [ + "specId": "Do all the things! - Spec", + "runOn": [ { - "app": { - "name": "chrome", - "path": "/usr/bin/firefox" - }, "platforms": [ "windows", "mac" - ] + ], + "browsers": { + "name": "firefox", + "window": {}, + "viewport": {} + } } ], "tests": [ { - "id": "Do all the things! - Test", + "testId": "Do all the things! - Test", "description": "This test includes nearly every property across all actions.", - "contexts": [ + "runOn": [ { - "app": { - "name": "firefox", - "path": "/usr/bin/firefox" - }, - "platforms": [ - "linux" - ] + "platforms": "linux", + "browsers": "firefox" } ], "steps": [ { - "action": "setVariables", - "path": ".env" + "loadVariables": ".env" }, { - "action": "runShell", - "command": "echo", - "args": [ - "$USER" - ] + "runShell": { + "command": "echo", + "args": [ + "$USER" + ], + "maxVariation": 0, + "overwrite": "aboveVariation" + }, + "variables": {} }, { - "action": "checkLink", - "url": "https://www.duckduckgo.com" + "checkLink": { + "url": "https://www.duckduckgo.com" + } }, { - "action": "httpRequest", - "url": "https://reqres.in/api/users", - "method": "post", - "requestData": { - "name": "morpheus", - "job": "leader" - }, - "responseData": { - "name": "morpheus", - "job": "leader" + "httpRequest": { + "method": "post", + "url": "https://reqres.in/api/users", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "statusCodes": [ + 200, + 201 + ], + "maxVariation": 0, + "overwrite": "aboveVariation" }, - "statusCodes": [ - 200, - 201 - ] + "variables": {} }, { - "action": "goTo", - "url": "https://www.duckduckgo.com" + "goTo": { + "url": "https://www.duckduckgo.com" + } }, { - "action": "find", - "selector": "[title=Search]", - "timeout": 10000, - "matchText": "Search", - "moveTo": true, - "click": true, - "typeKeys": { + "find": { + "selector": "[title=Search]", + "elementText": "Search", + "timeout": 10000, + "moveTo": true, + "click": true, + "type": { + "keys": [ + "shorthair cat" + ] + } + }, + "variables": {} + }, + { + "type": { "keys": [ - "shorthair cat" + "$ENTER$" ] } }, { - "action": "typeKeys", - "keys": [ - "$ENTER$" - ] - }, - { - "action": "saveScreenshot" + "screenshot": { + "maxVariation": 0, + "overwrite": "aboveVariation" + } } - ] + ], + "detectSteps": true } ] } @@ -128,25 +145,35 @@ tests | array of object([test](/docs/references/schemas/test)) | Required. [Tes ```json { - "id": "Make a request from an OpenAPI definition", + "specId": "Make a request from an OpenAPI definition", "openApi": [ { - "name": "Acme", "descriptionPath": "https://www.acme.com/openapi.json", - "server": "https://api.acme.com" + "server": "https://api.acme.com", + "name": "Acme" } ], "tests": [ { "steps": [ { - "action": "httpRequest", - "openApi": { - "operationId": "getUserById" + "httpRequest": { + "openApi": { + "operationId": "getUserById", + "validateAgainstSchema": "both", + "useExample": "none", + "exampleKey": "" + }, + "request": { + "parameters": { + "id": 123 + } + }, + "response": {}, + "maxVariation": 0, + "overwrite": "aboveVariation" }, - "requestParams": { - "id": 123 - } + "variables": {} } ] } diff --git a/docs/references/schemas/startRecording.md b/docs/references/schemas/startRecording.md deleted file mode 100644 index 0080a801..00000000 --- a/docs/references/schemas/startRecording.md +++ /dev/null @@ -1,39 +0,0 @@ - -# startRecording - -Start recording the current browser viewport. Must be followed by a `stopRecording` action. Only runs when the context `app` is `chrome` and `headless` is `false`. Supported extensions: [ '.mp4', '.webm', '.gif' ] - -## Fields - -Field | Type | Description | Default -:-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -path | string | Optional. File path of the recording. Supports the `.mp4`, `.webm`, and `.gif` extensions. If not specified, the file name is the ID of the step, and the extension is `.mp4`. | -directory | string | Optional. Directory of the file. Attempts to create the directory if it doesn't exist. | -overwrite | boolean | Optional. If `true`, overwrites the existing file at `path` if it exists. | `false` - -## Examples - -```json -{ - "action": "startRecording" -} -``` - -```json -{ - "action": "startRecording", - "path": "results.mp4" -} -``` - -```json -{ - "action": "startRecording", - "path": "results.mp4", - "directory": "static/media", - "overwrite": true -} -``` diff --git a/docs/references/schemas/step.md b/docs/references/schemas/step.md new file mode 100644 index 00000000..4d186043 --- /dev/null +++ b/docs/references/schemas/step.md @@ -0,0 +1,517 @@ + +# step + +A step in a test. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- +stepId | string | Optional. ID of the step. | +description | string | Optional. Description of the step. | +outputs | object | Optional. Outputs from step processes and user-defined expressions. Use the `outputs` object to reference outputs in subsequent steps. If a user-defined output matches the key for a step-defined output, the user-defined output takes precedence. | ``{}`` +variables | object | Optional. Environment variables to set from user-defined expressions. | ``{}`` +checkLink | One of
- string
- object | Optional. No description provided. | +click | One of
- string([Find element (simple)](/docs/references/schemas/Find element (simple)))
- object([Click element (detailed)](/docs/references/schemas/Click element (detailed)))
- boolean | Optional. Click or tap an element. | +find | One of
- string([Find element (simple)](/docs/references/schemas/Find element (simple)))
- object([Find element (detailed)](/docs/references/schemas/Find element (detailed))) | Optional. Find an element based on display text or a selector, then optionally interact with it. | +goTo | One of
- string
- object | Optional. No description provided. | +httpRequest | One of
- string(URL)
- object | Optional. Perform a generic HTTP request, for example to an API. | +runShell | One of
- string
- object | Optional. Perform a native shell command. | +runCode | object | Optional. Assemble and run code. | +runCode.language | string | Optional. Language of the code to run.

Accepted values: `python`, `bash`, `javascript` | +runCode.code | string | Optional. Code to run. | +runCode.args | array of string | Optional. Arguments for the command. | ``[]`` +runCode.workingDirectory | string | Optional. Working directory for the command. | `.` +runCode.exitCodes | array of integer | Optional. Expected exit codes of the command. If the command's actual exit code isn't in this list, the step fails. | ``[0]`` +runCode.stdio | string | Optional. Content expected in the command's output. If the expected content can't be found in the command's output (either stdout or stderr), the step fails. Supports strings and regular expressions. To use a regular expression, the string must start and end with a forward slash, like in `/^hello-world.*/`. | +runCode.path | string | Optional. File path to save the command's output, relative to `directory`. | +runCode.directory | string | Optional. Directory to save the command's output. If the directory doesn't exist, creates the directory. If not specified, the directory is your media directory. | +runCode.maxVariation | number | Optional. Allowed variation in percentage of text different between the current output and previously saved output. If the difference between the current output and the previous output is greater than `maxVariation`, the step fails. If output doesn't exist at `path`, this value is ignored. | `0` +runCode.overwrite | string | Optional. If `true`, overwrites the existing output at `path` if it exists. +If `aboveVariation`, overwrites the existing output at `path` if the difference between the new output and the existing output is greater than `maxVariation`.

Accepted values: `true`, `false`, `aboveVariation` | `aboveVariation` +runCode.timeout | integer | Optional. Max time in milliseconds the command is allowed to run. If the command runs longer than this, the step fails. | `60000` +type | object | Optional. Type keys. To type special keys, begin and end the string with `$` and use the special key's keyword. For example, to type the Escape key, enter `$ESCAPE$`. | +type.keys | One of
- string
- array of string | Optional. Sequence of keys to enter. | +type.inputDelay | number | Optional. Delay in milliseconds between each key press during a recording | `100` +type.selector | string | Optional. Selector for the element to type into. If not specified, the typing occurs in the active element. | +screenshot | One of
- string
- object
- boolean | Optional. Takes a screenshot in PNG format. | +record | One of
- string
- object
- boolean | Optional. Start recording the current browser viewport. Must be followed by a `stopRecord` step. Only runs in Chrome browsers when they are visible. Supported extensions: [ '.mp4', '.webm', '.gif' ] | +stopRecord | boolean | Optional. Stop the current recording. | +loadVariables | string | Optional. Load environment variables from the specified `.env` file. | +wait | One of
- number
- string
- boolean | Optional. Pause (in milliseconds) before performing the next action. | `5000` + +## Examples + +```json +{ + "stepId": "uuid", + "description": "Description of the step.", + "checkLink": "https://www.google.com", + "outputs": { + "outputKey": "outputValue" + }, + "variables": { + "variableKey": "variableValue" + } +} +``` + +```json +{ + "checkLink": "https://www.google.com" +} +``` + +```json +{ + "stepId": "path-only", + "checkLink": "/search" +} +``` + +```json +{ + "stepId": "status-code", + "checkLink": { + "url": "https://www.google.com", + "statusCodes": [ + 200 + ] + } +} +``` + +```json +{ + "goTo": { + "url": "https://www.google.com" + } +} +``` + +```json +{ + "goTo": "https://www.google.com" +} +``` + +```json +{ + "wait": 5000 +} +``` + +```json +{ + "runCode": { + "language": "python", + "code": "print('Hello from Python')", + "workingDirectory": ".", + "exitCodes": [ + 0 + ], + "stdio": "Hello from Python!", + "path": "python-output.txt", + "directory": "output", + "maxVariation": 0.1, + "overwrite": "aboveVariation" + } +} +``` + +```json +{ + "stopRecord": true +} +``` + +```json +{ + "screenshot": true +} +``` + +```json +{ + "screenshot": "image.png" +} +``` + +```json +{ + "screenshot": "static/images/image.png" +} +``` + +```json +{ + "screenshot": "/User/manny/projects/doc-detective/static/images/image.png" +} +``` + +```json +{ + "screenshot": { + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation", + "crop": "#elementToScreenshot" + } +} +``` + +```json +{ + "screenshot": { + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation" + } +} +``` + +```json +{ + "screenshot": { + "path": "image.png", + "directory": "static/images", + "maxVariation": 0.1, + "overwrite": "aboveVariation", + "crop": { + "selector": "#elementToScreenshot", + "elementText": "Element text", + "padding": { + "top": 0, + "right": 0, + "bottom": 0, + "left": 0 + } + } + } +} +``` + +```json +{ + "record": true +} +``` + +```json +{ + "record": "video.mp4" +} +``` + +```json +{ + "record": "static/media/video.mp4" +} +``` + +```json +{ + "record": "/User/manny/projects/doc-detective/static/media/video.mp4" +} +``` + +```json +{ + "record": { + "path": "video.mp4", + "directory": "static/media", + "overwrite": true + } +} +``` + +```json +{ + "loadVariables": "variables.env" +} +``` + +```json +{ + "find": "Find me!" +} +``` + +```json +{ + "find": { + "selector": "[title=Search]" + } +} +``` + +```json +{ + "find": { + "selector": "[title=Search]", + "timeout": 10000, + "elementText": "Search", + "moveTo": true, + "click": true, + "type": "shorthair cat" + } +} +``` + +```json +{ + "find": { + "selector": "[title=Search]", + "click": { + "button": "right" + } + } +} +``` + +```json +{ + "find": { + "selector": "[title=Search]", + "timeout": 10000, + "elementText": "Search", + "moveTo": true, + "click": true, + "type": { + "keys": [ + "shorthair cat" + ], + "inputDelay": 100 + } + } +} +``` + +```json +{ + "click": true +} +``` + +```json +{ + "click": "right" +} +``` + +```json +{ + "click": { + "button": "left", + "elementText": "Element text" + } +} +``` + +```json +{ + "click": { + "selector": "#elementToScreenshot", + "elementText": "Element text", + "button": "middle" + } +} +``` + +```json +{ + "httpRequest": "https://reqres.in/api/users" +} +``` + +```json +{ + "httpRequest": { + "url": "https://reqres.in/api/users" + } +} +``` + +```json +{ + "httpRequest": { + "url": "https://reqres.in/api/users/2", + "method": "put", + "request": { + "body": { + "name": "morpheus", + "job": "zion resident" + } + } + } +} +``` + +```json +{ + "httpRequest": { + "url": "https://reqres.in/api/users", + "method": "post", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "statusCodes": [ + 200, + 201 + ] + } +} +``` + +```json +{ + "httpRequest": { + "url": "https://www.api-server.com", + "method": "post", + "timeout": 30000, + "request": { + "body": { + "field": "value" + }, + "headers": { + "header": "value" + }, + "parameters": { + "param": "value" + } + }, + "response": { + "body": { + "field": "value" + }, + "headers": { + "header": "value" + } + }, + "statusCodes": [ + 200 + ] + } +} +``` + +```json +{ + "httpRequest": { + "url": "https://reqres.in/api/users", + "method": "post", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "statusCodes": [ + 200, + 201 + ], + "path": "response.json", + "directory": "media", + "maxVariation": 0.05, + "overwrite": "aboveVariation" + } +} +``` + +```json +{ + "httpRequest": { + "openApi": "getUserById" + } +} +``` + +```json +{ + "httpRequest": { + "openApi": { + "name": "Reqres", + "operationId": "getUserById" + }, + "request": { + "parameters": { + "id": 123 + } + } + } +} +``` + +```json +{ + "httpRequest": { + "openApi": { + "descriptionPath": "https://api.example.com/openapi.json", + "operationId": "getUserById" + }, + "request": { + "parameters": { + "id": 123 + } + } + } +} +``` + +```json +{ + "httpRequest": { + "openApi": { + "descriptionPath": "https://api.example.com/openapi.json", + "operationId": "createUser", + "useExample": "both" + } + } +} +``` + +```json +{ + "httpRequest": { + "openApi": { + "descriptionPath": "https://api.example.com/openapi.json", + "operationId": "updateUser", + "useExample": "request", + "exampleKey": "acme" + } + } +} +``` + +```json +{ + "httpRequest": { + "openApi": { + "descriptionPath": "https://api.example.com/openapi.json", + "operationId": "updateUser", + "useExample": "request", + "exampleKey": "acme", + "headers": { + "Authorization": "Bearer $TOKEN" + } + } + } +} +``` diff --git a/docs/references/schemas/stopRecord.md b/docs/references/schemas/stopRecord.md new file mode 100644 index 00000000..bb197d39 --- /dev/null +++ b/docs/references/schemas/stopRecord.md @@ -0,0 +1,15 @@ + +# stopRecord + +Stop the current recording. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- + +## Examples + +```json +true +``` diff --git a/docs/references/schemas/stopRecording.md b/docs/references/schemas/stopRecording.md deleted file mode 100644 index f5e8c593..00000000 --- a/docs/references/schemas/stopRecording.md +++ /dev/null @@ -1,20 +0,0 @@ - -# stopRecording - -Stop the current recording. - -## Fields - -Field | Type | Description | Default -:-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | - -## Examples - -```json -{ - "action": "stopRecording" -} -``` diff --git a/docs/references/schemas/test.md b/docs/references/schemas/test.md index 4c9ccff5..533c9fdf 100644 --- a/docs/references/schemas/test.md +++ b/docs/references/schemas/test.md @@ -7,15 +7,15 @@ A Doc Detective test. Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. Unique identifier for the test. | Generated UUID +testId | string | Optional. Unique identifier for the test. | Generated UUID description | string | Optional. Description of the test. | -file | string | Optional. Path to the file that the test is associated with. | -detectSteps | boolean | Optional. Whether or not to detect steps in input files based on markup regex. Defaults to `true`. | -contexts | array of object([context](/docs/references/schemas/context)) | Optional. Application/platform sets to run the test in. Overrides `contexts` defined at the config-level and spec-level. | -openApi | array of undefineds | Optional. undefined | -setup | string | Optional. Path to a test specification to perform before this test, while maintaining this test's context. Useful for setting up testing environments. Only the `steps` property is used from the first test in the setup spec. | -cleanup | string | Optional. Path to a test specification to perform after this test, while maintaining this test's context. Useful for cleaning up testing environments. Only the `steps` property is used from the first test in the cleanup spec. | -steps | array of
- object([checkLink](/docs/references/schemas/checkLink))
- object([goTo](/docs/references/schemas/goTo))
- object([httpRequest](/docs/references/schemas/httpRequest))
- object([runCode](/docs/references/schemas/runCode))
- object([runShell](/docs/references/schemas/runShell))
- object([saveScreenshot](/docs/references/schemas/saveScreenshot))
- object([setVariables](/docs/references/schemas/setVariables))
- object([startRecording](/docs/references/schemas/startRecording))
- object([stopRecording](/docs/references/schemas/stopRecording))
- object([typeKeys](/docs/references/schemas/typeKeys))
- object([find](/docs/references/schemas/find))
- object([wait](/docs/references/schemas/wait)) | Required. Actions to perform as part of the test. Performed in the sequence defined. If one or more actions fail, the test fails. | +contentPath | string | Optional. Path to the content that the test is associated with. | +detectSteps | boolean | Optional. Whether or not to detect steps in input files based on markup regex. | `true` +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. | +openApi | array of unknown | Optional. No description provided. | +before | string | Optional. Path to a test specification to perform before this test, while maintaining this test's context. Useful for setting up testing environments. Only the `steps` property is used from the first test in the setup spec. | +after | string | Optional. Path to a test specification to perform after this test, while maintaining this test's context. Useful for cleaning up testing environments. Only the `steps` property is used from the first test in the cleanup spec. | +steps | array of
one of:
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown
- unknown | Required. Steps to perform as part of the test. Performed in the sequence defined. If one or more actions fail, the test fails. By default, if a step fails, the test stops and the remaining steps are not executed. | ## Examples @@ -23,8 +23,7 @@ steps | array of
- object([checkLink](/docs/references/schemas/checkLi { "steps": [ { - "action": "checkLink", - "url": "https://www.duckduckgo.com" + "checkLink": "https://www.duckduckgo.com" } ] } @@ -34,18 +33,20 @@ steps | array of
- object([checkLink](/docs/references/schemas/checkLi { "steps": [ { - "action": "goTo", - "url": "https://www.duckduckgo.com" + "goTo": { + "url": "https://www.duckduckgo.com" + } }, { - "action": "find", - "selector": "[title=Search]", - "click": true, - "typeKeys": { - "keys": [ - "shorthair cats", - "$ENTER$" - ] + "find": { + "selector": "[title=Search]", + "click": true, + "type": { + "keys": [ + "shorthair cats", + "$ENTER$" + ] + } } } ] @@ -54,103 +55,139 @@ steps | array of
- object([checkLink](/docs/references/schemas/checkLi ```json { - "id": "Do all the things! - Test", + "testId": "Do all the things! - Test", "description": "This test includes every property across all actions.", - "contexts": [ + "before": "setup.json", + "after": "cleanup.json", + "runOn": [ { - "app": { - "name": "firefox", - "path": "/usr/bin/firefox" - }, "platforms": [ "linux" - ] + ], + "browsers": { + "name": "firefox", + "window": {}, + "viewport": {} + } } ], - "setup": "setup.json", - "cleanup": "cleanup.json", "steps": [ { - "action": "setVariables", - "path": ".env" + "loadVariables": ".env" }, { - "action": "runShell", - "command": "echo", - "args": [ - "$USER" - ] + "runShell": { + "command": "echo", + "args": [ + "$USER" + ], + "maxVariation": 0, + "overwrite": "aboveVariation" + }, + "variables": {} }, { - "action": "checkLink", - "url": "https://www.duckduckgo.com" + "checkLink": { + "url": "https://www.duckduckgo.com" + } }, { - "action": "httpRequest", - "url": "https://reqres.in/api/users", - "method": "post", - "requestData": { - "name": "morpheus", - "job": "leader" - }, - "responseData": { - "name": "morpheus", - "job": "leader" + "httpRequest": { + "method": "post", + "url": "https://reqres.in/api/users", + "request": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "response": { + "body": { + "name": "morpheus", + "job": "leader" + } + }, + "statusCodes": [ + 200, + 201 + ], + "maxVariation": 0, + "overwrite": "aboveVariation" }, - "statusCodes": [ - 200, - 201 - ] + "variables": {} + }, + { + "goTo": { + "url": "https://www.duckduckgo.com" + } }, { - "action": "goTo", - "url": "https://www.duckduckgo.com" + "find": { + "selector": "[title=Search]", + "elementText": "Search", + "timeout": 10000, + "moveTo": true, + "click": true, + "type": { + "keys": [ + "shorthair cat" + ] + } + }, + "variables": {} }, { - "action": "find", - "selector": "[title=Search]", - "timeout": 10000, - "matchText": "Search", - "moveTo": true, - "click": true, - "typeKeys": { + "type": { "keys": [ - "shorthair cat" + "$ENTER$" ] } }, { - "action": "typeKeys", - "keys": [ - "$ENTER$" - ] - }, - { - "action": "saveScreenshot" + "screenshot": { + "maxVariation": 0, + "overwrite": "aboveVariation" + } } - ] + ], + "detectSteps": true } ``` ```json { + "testId": "c61b02e8-7485-44d3-8065-f873673379c6", "openApi": [ { - "name": "Acme", "descriptionPath": "https://www.acme.com/openapi.json", - "server": "https://api.acme.com" + "server": "https://api.acme.com", + "validateAgainstSchema": "both", + "useExample": "none", + "exampleKey": "", + "name": "Acme" } ], "steps": [ { - "action": "httpRequest", - "openApi": { - "operationId": "getUserById" + "httpRequest": { + "openApi": { + "operationId": "getUserById", + "validateAgainstSchema": "both", + "useExample": "none", + "exampleKey": "" + }, + "request": { + "parameters": { + "id": 123 + } + }, + "response": {}, + "maxVariation": 0, + "overwrite": "aboveVariation" }, - "requestParams": { - "id": 123 - } + "variables": {} } - ] + ], + "detectSteps": true } ``` diff --git a/docs/references/schemas/type.md b/docs/references/schemas/type.md new file mode 100644 index 00000000..50bb8872 --- /dev/null +++ b/docs/references/schemas/type.md @@ -0,0 +1,55 @@ + +# type + +Type keys. To type special keys, begin and end the string with `$` and use the special key's keyword. For example, to type the Escape key, enter `$ESCAPE$`. + +## Fields + +Field | Type | Description | Default +:-- | :-- | :-- | :-- +keys | One of
- string
- array of string | Optional. Sequence of keys to enter. | +inputDelay | number | Optional. Delay in milliseconds between each key press during a recording | `100` +selector | string | Optional. Selector for the element to type into. If not specified, the typing occurs in the active element. | + +## Examples + +```json +"kittens" +``` + +```json +[ + "$ENTER$" +] +``` + +```json +[ + "kittens", + "$ENTER$" +] +``` + +```json +{ + "keys": "kittens" +} +``` + +```json +{ + "keys": [ + "$ENTER$" + ] +} +``` + +```json +{ + "keys": [ + "kittens", + "$ENTER$" + ], + "inputDelay": 500 +} +``` diff --git a/docs/references/schemas/typeKeys.md b/docs/references/schemas/typeKeys.md deleted file mode 100644 index e2242a98..00000000 --- a/docs/references/schemas/typeKeys.md +++ /dev/null @@ -1,43 +0,0 @@ - -# typeKeys - -Type keys. To type special keys, begin and end the string with `$` and use the special key's enum. For example, to type the Escape key, enter `$ESCAPE$`. - -## Fields - -Field | Type | Description | Default -:-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -keys | One of
- string
- array of strings | Required. String of keys to enter. | -delay | number | Optional. Delay in milliseconds between each key press. Only valid during a recording. | `100` - -## Examples - -```json -{ - "action": "typeKeys", - "keys": "kittens" -} -``` - -```json -{ - "action": "typeKeys", - "keys": [ - "$ENTER$" - ] -} -``` - -```json -{ - "action": "typeKeys", - "keys": [ - "kittens", - "$ENTER$" - ], - "delay": 500 -} -``` diff --git a/docs/references/schemas/wait.md b/docs/references/schemas/wait.md index dad1556b..37c143ae 100644 --- a/docs/references/schemas/wait.md +++ b/docs/references/schemas/wait.md @@ -1,28 +1,23 @@ # wait -Pause before performing the next action. +Pause (in milliseconds) before performing the next action. ## Fields Field | Type | Description | Default :-- | :-- | :-- | :-- -id | string | Optional. ID of the step. | Generated UUID -description | string | Optional. Description of the step. | -action | string | Required. The action to perform. | -duration | number | Optional. Milliseconds to wait. | `5000` ## Examples ```json -{ - "action": "wait" -} +5000 ``` ```json -{ - "action": "wait", - "duration": 5000 -} +"$WAIT_DURATION" +``` + +```json +true ``` diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 3b6c6c1c..96227339 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -74,13 +74,13 @@ const config: Config = { position: "left", label: "Get started", }, - { - type: "docSidebar", - sidebarId: "referencesSidebar", - position: "left", - label: "References", - }, - { to: "/app", label: "Action Builder (beta)", position: "left" }, + // { + // type: "docSidebar", + // sidebarId: "referencesSidebar", + // position: "left", + // label: "References", + // }, + // { to: "/app", label: "Action Builder (beta)", position: "left" }, // {to: '/blog', label: 'Blog', position: 'left'}, { to: "/support", label: "Support ❤️", position: "right" }, { diff --git a/package-lock.json b/package-lock.json index 6e64f47d..5b022f16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "doc-detective-docs", - "version": "2.0.6", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "doc-detective-docs", - "version": "2.0.6", + "version": "3.0.0", "dependencies": { "@docusaurus/core": "3.7.0", "@docusaurus/preset-classic": "3.7.0", @@ -17,21 +17,21 @@ "@mui/icons-material": "^6.4.3", "@mui/material": "^6.4.3", "clsx": "^2.1.1", - "doc-detective-common": "^1.22.0", - "dotenv": "^16.4.7", + "doc-detective-common": "^3.0.2", + "dotenv": "^16.5.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", - "posthog-docusaurus": "^2.0.2", + "posthog-docusaurus": "^2.0.4", "prism-react-renderer": "^2.4.1", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", "react-markdown": "^9.0.3", "stream-http": "^3.2.0", - "yaml": "^2.7.0" + "yaml": "^2.7.1" }, "devDependencies": { - "@chromatic-com/storybook": "^3.2.4", + "@chromatic-com/storybook": "^3.2.6", "@docusaurus/module-type-aliases": "3.7.0", "@docusaurus/tsconfig": "3.7.0", "@docusaurus/types": "3.7.0", @@ -304,9 +304,9 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.0.tgz", - "integrity": "sha512-8Q/r5mXLa8Rfyh6r4SgEEFJgISVN5cDNFlcfSWLgFn3odzQhTfHAqzI3hMGdcROViL+8NrDNVVFQtEUrYOksDg==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-12.0.1.tgz", + "integrity": "sha512-D3n3T19ly/RYyEvsrqKa/cSAlzQF8KXj1o0XLTI+9GKnJvdHT3WW50yZYCf+4JU7+pLlhXZVV1q1AJ0SPN/pmw==", "license": "MIT", "dependencies": { "@jsdevtools/ono": "^7.1.3", @@ -2002,9 +2002,9 @@ "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" }, "node_modules/@chromatic-com/storybook": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.4.tgz", - "integrity": "sha512-5/bOOYxfwZ2BktXeqcCpOVAoR6UCoeART5t9FVy22hoo8F291zOuX4y3SDgm10B1GVU/ZTtJWPT2X9wZFlxYLg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.6.tgz", + "integrity": "sha512-FDmn5Ry2DzQdik+eq2sp/kJMMT36Ewe7ONXUXM2Izd97c7r6R/QyGli8eyh/F0iyqVvbLveNYFyF0dBOJNwLqw==", "dev": true, "license": "MIT", "dependencies": { @@ -7514,9 +7514,9 @@ } }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -10162,34 +10162,19 @@ } }, "node_modules/doc-detective-common": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/doc-detective-common/-/doc-detective-common-1.22.0.tgz", - "integrity": "sha512-l/mp8iow96QhDESJKLwo45IwaP1//5k3EJgJpJuN0E+VC72ZvpFqeaKRqUZXVk3KgDwKPCQdtgvOJ6mhnGP0YA==", - "license": "MIT", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/doc-detective-common/-/doc-detective-common-3.0.2.tgz", + "integrity": "sha512-Gh5EAwwuG7aN9FBKUt1pj4zUuWPBCCz7SL3McjeWYjtvmdGbQOg4XHAESK87INC4tZLIHm+7snymXv9VkP8QTw==", + "license": "AGPL-3.0-only", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^11.9.0", - "ajv": "8.16.0", + "@apidevtools/json-schema-ref-parser": "^12.0.1", + "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-formats": "^3.0.1", "ajv-keywords": "^5.1.0", - "axios": "^1.7.9", - "uuid": "^11.0.5", - "yaml": "^2.7.0" - } - }, - "node_modules/doc-detective-common/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "axios": "^1.8.4", + "uuid": "^11.1.0", + "yaml": "^2.7.1" } }, "node_modules/doc-detective-common/node_modules/ajv-formats": { @@ -10209,9 +10194,9 @@ } }, "node_modules/doc-detective-common/node_modules/uuid": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", - "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -10355,9 +10340,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -18818,9 +18803,9 @@ } }, "node_modules/posthog-docusaurus": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/posthog-docusaurus/-/posthog-docusaurus-2.0.2.tgz", - "integrity": "sha512-RUxVzJqZ214JuEr1msngxXgTYlK+q37Vhhvp4yG07K+UFF12HBU1TrOdl/MEmvHQimsFQNEa68eYasJo2kAsJA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/posthog-docusaurus/-/posthog-docusaurus-2.0.4.tgz", + "integrity": "sha512-xnEVCBovSuvQvYXGny03CDTc0yZCl7O3Mz21sJpXmE1Gvs21gM33WzWaA9Cm6WvWGYZtQy8t8/g8OjCkmTWlXA==", "license": "MIT", "engines": { "node": ">=10.15.1" @@ -19128,9 +19113,9 @@ } }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -19366,15 +19351,15 @@ } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", "dependencies": { - "scheduler": "^0.25.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^19.1.0" } }, "node_modules/react-error-overlay": { @@ -20304,9 +20289,9 @@ "license": "ISC" }, "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, "node_modules/schema-utils": { @@ -23041,9 +23026,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 3bf28f2d..760465c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "doc-detective-docs", - "version": "2.0.6", + "version": "3.0.0", "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start", @@ -28,21 +28,21 @@ "@mui/icons-material": "^6.4.3", "@mui/material": "^6.4.3", "clsx": "^2.1.1", - "doc-detective-common": "^1.22.0", - "dotenv": "^16.4.7", + "doc-detective-common": "^3.0.2", + "dotenv": "^16.5.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", - "posthog-docusaurus": "^2.0.2", + "posthog-docusaurus": "^2.0.4", "prism-react-renderer": "^2.4.1", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", "react-markdown": "^9.0.3", "stream-http": "^3.2.0", - "yaml": "^2.7.0" + "yaml": "^2.7.1" }, "devDependencies": { - "@chromatic-com/storybook": "^3.2.4", + "@chromatic-com/storybook": "^3.2.6", "@docusaurus/module-type-aliases": "3.7.0", "@docusaurus/tsconfig": "3.7.0", "@docusaurus/types": "3.7.0", diff --git a/sidebars.ts b/sidebars.ts index 776c78de..2a9e67a7 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -36,15 +36,16 @@ const sidebars: SidebarsConfig = { link: {type: 'generated-index', description: 'Actions are the commands performed in each step of a test. Each action has a specific purpose, such as checking a link, finding an element, or navigating to a URL.'}, items: [ 'get-started/actions/checkLink', + 'get-started/actions/click', 'get-started/actions/find', 'get-started/actions/goTo', 'get-started/actions/httpRequest', 'get-started/actions/runShell', - 'get-started/actions/saveScreenshot', - 'get-started/actions/setVariables', - 'get-started/actions/startRecording', - 'get-started/actions/stopRecording', - 'get-started/actions/typeKeys', + 'get-started/actions/screenshot', + 'get-started/actions/loadVariables', + 'get-started/actions/record', + 'get-started/actions/stopRecord', + 'get-started/actions/type', 'get-started/actions/wait' ] }