Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,24 @@ dist
*.mp4

# Browser snapshots
browser-snapshots
browser-snapshots

# Doc Detective test artifacts
test/artifacts/output/*.txt
test/artifacts/output/*.gif
test/artifacts/output/*.png
test/artifacts/output/*.mp4
test/artifacts/output/*.mkv
test/artifacts/output/*.spec.json
test/artifacts/output/*.report.json
test/artifacts/output/safari-screenshot-*.png
test/artifacts/output/screenshot-*.png
test/artifacts/output/firefox-screenshot-*.png
test/artifacts/output/chrome-screenshot-*.png
.appium/
Browserstack/

# Debug demo files
debug-*.js
debug-*.spec.json
normal-*.js
122 changes: 122 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,128 @@ const { runTests, runCoverage } = require("doc-detective-core");

Run test specifications. Returns a test report object. Takes [`config`](https://doc-detective.com/reference/schemas/config.html) as input. Parses paths in the `config.input` for test specifications to perform.

## Debug Mode

Doc Detective Core supports a debug step-through mode that allows you to run tests one step at a time, waiting for user input before proceeding to the next step. This is particularly useful for:

- Debugging test failures
- Understanding test execution flow
- Manually verifying each step during development

### Enabling Debug Mode

Enable step-through debug mode by setting the `debug` configuration option:

```javascript
const { runTests } = require("doc-detective-core");

const config = {
input: "path/to/your/tests",
debug: true // or debug: "stepThrough"
};

const results = await runTests(config);
```

### Debug Features

**Step-Through Mode**: When enabled, the test execution will pause before each step and display:
- Current context and step information
- Step description and action type
- Step variables that will be set (if any)
- Interactive prompt for user input

**Auto-Break on Failure**: Debug mode automatically pauses when a step fails, allowing you to inspect the failure before continuing.

**Sequential Execution**: Debug mode forces `concurrentTests` to 1 for sequential execution to ensure proper step-through behavior.

**Interactive Controls**: During debug pauses, you can:
- Press `c` or type `continue` to proceed to the next step
- Press `q` or type `quit` to stop test execution
- Press `v` or type `view` to display available variables and their values
- Press `e` or type `evaluate` to interactively evaluate expressions with current context
- Press `s` or type `set` to set environment variables for testing

**Variable Inspection**: View and interact with the test execution context:
- Environment variables (with truncated display for long values)
- Meta values and hierarchical test structure
- Step outputs from previous actions
- Interactive expression evaluation using Doc Detective's expression syntax

**Non-Interactive Support**: In non-interactive environments (CI/CD, scripts), debug mode will automatically continue without pausing, allowing tests to run normally while still logging debug information.

### Example Debug Session

```
--- DEBUG STEP-THROUGH MODE ---
⏸️ Step-through mode: Paused before next step
Context: my-test-context
Step ID: step-1
Step Description: Click the login button
Step Action: click

Options:
[c] Continue to next step
[q] Quit execution
[v] View available variables
[e] Evaluate expression
[s] Set environment variable
Choice: v

=== AVAILABLE VARIABLES ===

--- Environment Variables ---
NODE_ENV: development
PATH: /usr/local/bin:/usr/bin:/bin
... and 15 more environment variables

--- Meta Values (Test Execution Context) ---
{
"specs": {
"test-spec": {
"tests": {
"my-test": {
"contexts": {
"my-test-context": {
"steps": {}
}
}
}
}
}
}
}

--- Recent Step Outputs ---
No step outputs available yet

Tip: Use expressions like $$specs.specId.tests.testId.contexts.contextId.steps.stepId.outputs.key
Or environment variables like $VARIABLE_NAME

Options:
[c] Continue to next step
[q] Quit execution
[v] View available variables
[e] Evaluate expression
[s] Set environment variable
Choice: c
```

### Current Features

The debug system includes these implemented features:
- **Step-Through Mode**: Pause before each step execution
- **Auto-Break on Failure**: Automatically pause when steps fail
- **Variable Inspection**: View environment variables, meta values, and step outputs
- **Expression Evaluation**: Test expressions interactively with current context
- **Environment Variable Setting**: Modify variables during debugging sessions
- **Sequential Execution**: Forces single-threaded execution for predictable debugging

### Future Enhancements

Additional features planned for future releases:
- **Breakpoints**: Pause at specific step IDs or conditions

## Contributions

Looking to help out? See our [contributions guide](https://github.com/doc-detective/doc-detective-core/blob/main/CONTRIBUTIONS.md) for more info. If you can't contribute code, you can still help by reporting issues, suggesting new features, improving the documentation, or sponsoring the project.
28 changes: 28 additions & 0 deletions debug-demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const { runTests } = require("./src/index");

async function testDebugMode() {
console.log("=== Testing Debug Step-Through Mode ===\n");

// Test with step-through mode enabled
const config = {
input: "debug-demo.spec.json",
logLevel: "info",
debug: "stepThrough"
};

console.log("Running test with debug step-through mode enabled...");
console.log("Config:", JSON.stringify(config, null, 2));
console.log("\nStarting test execution...\n");

try {
const results = await runTests(config);
console.log("\n=== Debug Test Complete ===");
if (results) {
console.log("Results summary:", results.summary);
}
} catch (error) {
console.error("Error during test execution:", error);
}
}

testDebugMode();
23 changes: 23 additions & 0 deletions debug-demo.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"tests": [
{
"steps": [
{
"stepId": "step-1",
"description": "First test step",
"runShell": "echo 'Step 1: Hello from debug mode'"
},
{
"stepId": "step-2",
"description": "Second test step",
"runShell": "echo 'Step 2: This is the second step'"
},
{
"stepId": "step-3",
"description": "Third test step",
"runShell": "echo 'Step 3: Final step'"
}
]
}
]
}
85 changes: 85 additions & 0 deletions debug-direct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const { executeTestContext } = require("./src/tests");

async function testDebugDirectly() {
console.log("=== Testing Debug Mode Directly ===\n");

// Create a mock config with debug enabled internally
const config = {
logLevel: "info",
debug: "stepThrough",
_debugParsed: {
stepThrough: true,
breakOnFail: false,
breakpoints: []
}
};

const context = {
contextId: "debug-test-context",
steps: [
{
stepId: "step-1",
description: "First test step",
runShell: "echo 'Step 1: Hello from debug mode'"
},
{
stepId: "step-2",
description: "Second test step",
runShell: "echo 'Step 2: This is the second step'"
},
{
stepId: "step-3",
description: "Third test step",
runShell: "echo 'Step 3: Final step'"
}
]
};

const spec = { specId: "debug-test-spec" };
const test = { testId: "debug-test-test" };
const runnerDetails = {
environment: { platform: "linux" },
availableApps: [],
allowUnsafeSteps: true
};
const metaValues = {
specs: {
"debug-test-spec": {
tests: {
"debug-test-test": {
contexts: {
"debug-test-context": { steps: {} }
}
}
}
}
}
};

console.log("Starting debug test execution...");
console.log("This will pause at each step if you're in an interactive terminal.\n");

try {
const result = await executeTestContext({
context,
config,
spec,
test,
runnerDetails,
availableApps: [],
platform: "linux",
metaValues,
});

console.log("\n=== Debug Test Complete ===");
console.log("Result:", result.contextReport.result);
console.log("Steps executed:", result.contextReport.steps.length);
result.contextReport.steps.forEach((step, index) => {
console.log(` Step ${index + 1}: ${step.result} - ${step.description}`);
});
} catch (error) {
console.error("Error during test execution:", error);
}
}

testDebugDirectly();
92 changes: 92 additions & 0 deletions debug-non-interactive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const { executeTestContext } = require("./src/tests");

async function testDebugNonInteractive() {
console.log("=== Testing Debug Mode (Non-Interactive) ===\n");

// Temporarily disable TTY to simulate non-interactive environment
const originalIsTTY = process.stdin.isTTY;
process.stdin.isTTY = false;

try {
// Create a mock config with debug enabled internally
const config = {
logLevel: "info",
debug: "stepThrough",
_debugParsed: {
stepThrough: true,
breakOnFail: false,
breakpoints: []
}
};

const context = {
contextId: "debug-test-context",
steps: [
{
stepId: "step-1",
description: "First test step",
runShell: "echo 'Step 1: Hello from debug mode'"
},
{
stepId: "step-2",
description: "Second test step",
runShell: "echo 'Step 2: This is the second step'"
},
{
stepId: "step-3",
description: "Third test step",
runShell: "echo 'Step 3: Final step'"
}
]
};

const spec = { specId: "debug-test-spec" };
const test = { testId: "debug-test-test" };
const runnerDetails = {
environment: { platform: "linux" },
availableApps: [],
allowUnsafeSteps: true
};
const metaValues = {
specs: {
"debug-test-spec": {
tests: {
"debug-test-test": {
contexts: {
"debug-test-context": { steps: {} }
}
}
}
}
}
};

console.log("Starting debug test execution (non-interactive)...");
console.log("In non-interactive mode, debug pauses will auto-continue.\n");

const result = await executeTestContext({
context,
config,
spec,
test,
runnerDetails,
availableApps: [],
platform: "linux",
metaValues,
});

console.log("\n=== Debug Test Complete ===");
console.log("Result:", result.contextReport.result);
console.log("Steps executed:", result.contextReport.steps.length);
result.contextReport.steps.forEach((step, index) => {
console.log(` Step ${index + 1}: ${step.result} - ${step.description}`);
});
} catch (error) {
console.error("Error during test execution:", error);
} finally {
// Restore TTY setting
process.stdin.isTTY = originalIsTTY;
}
}

testDebugNonInteractive();
Loading