Skip to content
Closed
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
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The main dashboard now exposes a test-selection panel before execution:
- The results view also has live search for APIs, endpoints, tests, assertions, and error text.
- Matching text is highlighted in both selection and result sections to make hits easier to spot.
- Environment, endpoint, and individual test checkboxes let you run only the subset you care about.
- Each endpoint row in test selection also exposes an `Edit endpoint` action that opens the cURL import page preloaded with that endpoint's current request and tests.
- Each page load starts with all tests selected by default.
- The dashboard and cURL import pages use local AdminLTE assets, so the UI does not rely on external CDNs at runtime.

Expand All @@ -112,7 +113,9 @@ The tool will:

- parse the request URL, method, headers, query string, and JSON body
- optionally parse a pasted JSON response body in the same flow so the assertion builder stays on the same page
- let you add assertion drafts first, then use the main `Analyze and Generate` action to include them in the endpoint YAML preview
- let you create multiple drafted tests, each with its own expected status and assertion set, then use `Analyze and Generate` to include all of them in the endpoint YAML preview
- open directly from the dashboard's `Edit endpoint` action so existing endpoint requests and tests can be adjusted in the importer flow
- save an edited endpoint back to its owning endpoint YAML file with `Save Endpoint YAML`
- scan the configured YAML suite for an existing environment whose `baseUrl` already covers the pasted request URL
- scan for an existing endpoint with the same method and either the same relative path or a matching path template such as `/customers/{customerId}`
- generate suggested environment YAML when the base URL is not already present
Expand All @@ -125,7 +128,9 @@ Current first-version scope:

- best support is for common `curl`, `-X`, `-H`, `--data`, `--data-raw`, `--data-binary`, and `--url` forms
- the page now uses one main `Analyze and Generate` action instead of separate request-analysis and response-parse steps
- each drafted test in the cURL page keeps its own assertions, so you can generate multiple YAML tests for one endpoint in a single pass
- generated YAML is shown as preview text, not written directly to disk
- when editing an existing endpoint from the dashboard, the cURL importer can now write the updated endpoint back to its original YAML file
- the assertion builder currently targets the most common field assertions: `equals`, `notEquals`, `type`, `containsText`, `startsWith`, `endsWith`, `notEmpty`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`, `minCount`, `maxCount`, and `count`
- missing YAML warnings are surfaced in the cURL import UI, but malformed YAML content still needs to be fixed before the dashboard runner can execute the suite

Expand Down Expand Up @@ -300,6 +305,12 @@ assertions:
- field: data.accounts
contains:
status: Active

- field: data.accounts
contains:
portfolioValue:
greaterThan: 1000
status: Active
```

## Dynamic parameters and variables
Expand Down
22 changes: 22 additions & 0 deletions src/ApiTestRunner.App/Models/CurlContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ public sealed class CurlAnalyzeRequest
{
public string Command { get; init; } = string.Empty;

public string? EnvironmentId { get; init; }

public string? EndpointName { get; init; }

public string? ResponseBody { get; init; }

public IReadOnlyList<CurlTestDraft> Tests { get; init; } = [];

public IReadOnlyList<CurlAssertionDraft> Assertions { get; init; } = [];
}

Expand Down Expand Up @@ -52,6 +58,15 @@ public sealed class CurlAssertionDraft
public object? Value { get; init; }
}

public sealed class CurlTestDraft
{
public string Name { get; init; } = string.Empty;

public int ExpectedStatus { get; init; } = 200;

public IReadOnlyList<CurlAssertionDraft> Assertions { get; init; } = [];
}

public sealed class CurlEnvironmentAnalysis
{
public bool Exists { get; init; }
Expand Down Expand Up @@ -94,6 +109,13 @@ public sealed class CurlEndpointAnalysis
public string? DiffYaml { get; init; }
}

public sealed class CurlYamlPreview
{
public string Title { get; init; } = string.Empty;

public string Yaml { get; init; } = string.Empty;
}

public sealed class CurlVariableAnalysis
{
public bool HasSuggestions { get; init; }
Expand Down
43 changes: 43 additions & 0 deletions src/ApiTestRunner.App/Models/DashboardContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,49 @@ public sealed class DashboardEndpointManifest
public IReadOnlyList<DashboardTestManifest> Tests { get; init; } = [];
}

public sealed class DashboardEndpointEditorSeed
{
public string EnvironmentId { get; init; } = string.Empty;

public string EnvironmentName { get; init; } = string.Empty;

public string EndpointId { get; init; } = string.Empty;

public string EndpointName { get; init; } = string.Empty;

public string? SourceFilePath { get; init; }

public string CurlCommand { get; init; } = string.Empty;

public IReadOnlyList<CurlTestDraft> Tests { get; init; } = [];
}

public sealed class DashboardEndpointSaveRequest
{
public string EnvironmentId { get; init; } = string.Empty;

public string EndpointId { get; init; } = string.Empty;

public string EndpointName { get; init; } = string.Empty;

public string Command { get; init; } = string.Empty;

public IReadOnlyList<CurlTestDraft> Tests { get; init; } = [];
}

public sealed class DashboardEndpointSaveResponse
{
public string EnvironmentId { get; init; } = string.Empty;

public string EndpointId { get; init; } = string.Empty;

public string EndpointName { get; init; } = string.Empty;

public string FilePath { get; init; } = string.Empty;

public DateTimeOffset SavedAtUtc { get; init; }
}

public sealed class DashboardTestManifest
{
public string Id { get; init; } = string.Empty;
Expand Down
34 changes: 34 additions & 0 deletions src/ApiTestRunner.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
builder.Services.AddApiTestRunnerCore();
builder.Services.AddSingleton<IConfiguredTestSuiteProvider, ConfiguredTestSuiteProvider>();
builder.Services.AddSingleton<ICurlCommandAnalyzer, CurlCommandAnalyzer>();
builder.Services.AddSingleton<DashboardEndpointEditorService>();
builder.Services.AddSingleton<TestRunCoordinator>();
builder.Services.AddSingleton<CliResultWriter>();
if (cliExecutionOptions.Enabled)
Expand Down Expand Up @@ -62,6 +63,39 @@
}
});

app.MapGet("/api/dashboard/editor-seed", async (
string environmentId,
string endpointId,
TestRunCoordinator coordinator,
CancellationToken cancellationToken) =>
{
try
{
var seed = await coordinator.GetEditorSeedAsync(environmentId, endpointId, cancellationToken);
return Results.Ok(seed);
}
catch (Exception exception)
{
return Results.BadRequest(new { error = exception.Message });
}
});

app.MapPost("/api/dashboard/editor-save", async (
DashboardEndpointSaveRequest request,
TestRunCoordinator coordinator,
CancellationToken cancellationToken) =>
{
try
{
var response = await coordinator.SaveEditorAsync(request, cancellationToken);
return Results.Ok(response);
}
catch (Exception exception)
{
return Results.BadRequest(new { error = exception.Message });
}
});

app.MapPost("/api/dashboard/run", async (HttpRequest request, TestRunCoordinator coordinator, CancellationToken cancellationToken) =>
{
var selection = request.ContentLength > 0
Expand Down
Loading
Loading