FlaUIQATouch 1.1.5
dotnet tool install --global FlaUIQATouch --version 1.1.5
dotnet new tool-manifest
dotnet tool install --local FlaUIQATouch --version 1.1.5
#tool dotnet:?package=FlaUIQATouch&version=1.1.5
nuke :add-package FlaUIQATouch --version 1.1.5
FlaUIQATouch (C#)
QA Touch reporter for FlaUI (.NET desktop UI automation via NUnit / MSTest).
Reads NUnit3 XML or TRX result files produced bydotnet test/nunit-console
and automatically creates modules, test cases, and a test run in QA Touch.
Ships as a single NuGet package — usable both as a library reference and adotnet toolCLI.
Table of Contents
- Features
- Prerequisites
- Install
- Quick Start
- Library Usage
- CLI Usage
- Configuration Options
- Supported Result Formats
- How It Works
- BDD Mode
- Project Structure
- CI/CD Examples
- API Reference
- Troubleshooting
- License
Features
- Parses NUnit3 XML and TRX (MSTest / VSTest) result files
- Auto-creates modules (test suites) in QA Touch from test class names
- Auto-creates test cases with pre-built step templates
- Creates a test run and uploads all results in one go
- Supports bulk result upload with automatic fallback to individual uploads
- Attaches error messages as comments on failed test results
- Resolves or creates milestones automatically
- Single package — works as both a NuGet library and a
dotnet toolCLI - Targets .NET 8.0+ (compatible with .NET 8, 9, 10, and later)
- Built-in 429 rate-limit retry with exponential backoff (3 retries, 500ms base)
- Input validation — clear errors for missing or empty configuration
Prerequisites
- .NET SDK 8.0 or later (for building / CLI)
- A QA Touch account with:
- Your subdomain (e.g.
mycompanyfrommycompany.qatouch.com) - An API token (Settings > API Token in QA Touch)
- A project key (visible in QA Touch project URL)
- A user key for test run assignment
- Your subdomain (e.g.
Install
As a CLI Tool (recommended for CI/CD)
dotnet tool install --global FlaUIQATouch
This gives you the flaui-qatouch-upload command.
As a Library Reference
dotnet add package FlaUIQATouch
For programmatic use in your .NET 8.0+ projects.
Quick Start
# 1. Run your FlaUI tests and produce results
dotnet test --logger "nunit;LogFilePath=TestResults/results.xml"
# 2. Upload results to QA Touch
flaui-qatouch-upload \
--results TestResults/results.xml \
--domain mycompany \
--token YOUR_API_TOKEN \
--project PROJ \
--assign user-key
# 3. Or upload as BDD (Gherkin) test cases
flaui-qatouch-upload \
--results TestResults/results.xml \
--domain mycompany \
--token YOUR_API_TOKEN \
--project PROJ \
--assign user-key \
--bdd
Library Usage
Add the NuGet package to your project, then use FlaUIQATouchReporter to upload results programmatically:
using FlaUIQATouch;
// Configure the reporter
using var reporter = new FlaUIQATouchReporter(new FlaUIQATouchReporterOptions
{
Domain = "mycompany", // QA Touch subdomain
ApiToken = "YOUR_API_TOKEN", // QA Touch API token
ProjectKey = "PROJ", // QA Touch project key
AssignTo = "user-key", // user key for test run
ResultsFile = @".\TestResults\results.xml", // NUnit3 XML or .trx
MilestoneName = "Sprint 12", // auto-created if missing
Tag = "flaui", // tag on the test run
});
// Parse results + create modules/cases/run + upload
await reporter.RunAsync();
Example: BDD Mode
Set BddMode = true to create missing test cases as BDD (Gherkin) scenarios via the QA Touch /testCase/bdd endpoint instead of the standard step-based endpoint:
using FlaUIQATouch;
using var reporter = new FlaUIQATouchReporter(new FlaUIQATouchReporterOptions
{
Domain = "mycompany",
ApiToken = "YOUR_API_TOKEN",
ProjectKey = "PROJ",
AssignTo = "user-key",
ResultsFile = @".\TestResults\results.xml",
BddMode = true, // <-- creates BDD cases with Gherkin scenarios
});
await reporter.RunAsync();
Each auto-created test case will contain a Gherkin scenario like:
Scenario: Verify instrument name is displayed
GIVEN the application is launched and ready
WHEN I execute the test: Verify instrument name is displayed
THEN it should complete successfully
Example: Post-test upload in a console app
// UploadResults.csproj — a small console app
using FlaUIQATouch;
var options = new FlaUIQATouchReporterOptions
{
Domain = Environment.GetEnvironmentVariable("QATOUCH_DOMAIN")!,
ApiToken = Environment.GetEnvironmentVariable("QATOUCH_TOKEN")!,
ProjectKey = Environment.GetEnvironmentVariable("QATOUCH_PROJECT")!,
AssignTo = Environment.GetEnvironmentVariable("QATOUCH_ASSIGN")!,
ResultsFile = args.Length > 0 ? args[0] : "TestResults/results.xml",
};
using var reporter = new FlaUIQATouchReporter(options);
await reporter.RunAsync();
dotnet test --logger "nunit;LogFilePath=TestResults/results.xml"
dotnet run --project UploadResults.csproj -- TestResults/results.xml
CLI Usage
flaui-qatouch-upload --results <path> --domain <domain> --token <token>
--project <key> --assign <user-key> [options]
Examples
# NUnit3 XML results
flaui-qatouch-upload \
--results TestResults/results.xml \
--domain mycompany \
--token abc123 \
--project PROJ \
--assign user-key
# TRX results with custom milestone
flaui-qatouch-upload \
--results TestResults/results.trx \
--domain mycompany \
--token abc123 \
--project PROJ \
--assign user-key \
--milestone-name "Release 2.0" \
--tag regression
# BDD mode — create Gherkin scenarios in QA Touch
flaui-qatouch-upload \
--results TestResults/results.xml \
--domain mycompany \
--token abc123 \
--project PROJ \
--assign user-key \
--bdd
# Use an existing module and milestone (skip auto-creation)
flaui-qatouch-upload \
--results results.xml \
--domain mycompany \
--token abc123 \
--project PROJ \
--assign user-key \
--testsuite MOD-123 \
--milestone-key MIL-456 \
--no-create-cases
CLI Help
flaui-qatouch-upload --help
Configuration Options
Library Options (FlaUIQATouchReporterOptions)
| Property | Type | Default | Required | Description |
|---|---|---|---|---|
Domain |
string |
— | Yes | QA Touch subdomain (e.g. mycompany) |
ApiToken |
string |
— | Yes | QA Touch API token |
ProjectKey |
string |
— | Yes | QA Touch project key |
AssignTo |
string |
— | Yes | User key to assign the test run to |
ResultsFile |
string |
— | Yes | Path to NUnit3 XML or TRX file |
TestsuiteId |
string? |
null |
No | Fixed module key (skips auto-creation of modules) |
MilestoneName |
string |
"FlaUI Automation" |
No | Milestone name (reused if exists, created if not) |
MilestoneKey |
string? |
null |
No | Existing milestone key (skips milestone lookup/creation) |
CreateCases |
bool |
true |
No | Auto-create test cases that don't exist in QA Touch |
BddMode |
bool |
false |
No | Create missing test cases as BDD (Gherkin) type instead of step-based |
Tag |
string |
"flaui" |
No | Tag applied to the test run |
CLI Arguments
| Argument | Required | Description |
|---|---|---|
--results <path> |
Yes | Path to NUnit3 XML or TRX results file |
--domain <domain> |
Yes | QA Touch subdomain |
--token <token> |
Yes | QA Touch API token |
--project <key> |
Yes | QA Touch project key |
--assign <user-key> |
Yes | User key to assign the test run |
--testsuite <key> |
No | Fixed module key (skips auto-create) |
--milestone-name <name> |
No | Milestone name (default: "FlaUI Automation") |
--milestone-key <key> |
No | Existing milestone key (skips creation) |
--tag <tag> |
No | Tag applied to the test run (default: "flaui") |
--no-create-cases |
No | Do not auto-create missing test cases |
--bdd |
No | Create missing test cases as BDD (Gherkin) type |
--help, -h |
No | Show help message |
Supported Result Formats
| Format | How to Produce | File Extension |
|---|---|---|
| NUnit3 XML | dotnet test --logger "nunit;LogFilePath=results.xml" |
.xml |
| NUnit3 XML | nunit-console --result=results.xml |
.xml |
| TRX (MSTest/VSTest) | dotnet test --logger "trx;LogFileName=results.trx" |
.trx |
Note: For NUnit3 XML output with
dotnet test, install the NUnit3TestAdapter and NunitXml.TestLogger NuGet packages in your test project.
How It Works
The reporter follows this workflow when RunAsync() is called:
- Parse — Reads the results file and extracts test names, suite names, statuses, and error messages
- Resolve modules — Groups tests by suite name; looks up existing modules in QA Touch or creates new ones
- Resolve test cases — Matches parsed test names to existing QA Touch test cases (by normalized title); creates missing cases with step templates if
CreateCasesistrue - Resolve milestone — Uses the provided
MilestoneKey, or finds/creates a milestone byMilestoneName - Create test run — Creates a new test run in QA Touch linked to the milestone with all mapped case keys
- Upload results — Posts results in bulk; failed tests with error messages are uploaded individually with comments; falls back to individual uploads on bulk failure
Status Mapping
| Test Result | QA Touch Status | Status ID |
|---|---|---|
| Passed | passed | 1 |
| Failed / Error | failed | 5 |
| Ignored / Inconclusive / NotRunnable | blocked | 3 |
| Other | untested | 2 |
BDD Mode
BDD Mode changes how missing test cases are created in QA Touch. Instead of creating standard step-based test cases, it uses the QA Touch BDD API (POST /testCase/bdd) to create cases with Gherkin scenarios.
When to Use BDD Mode
- Your tests follow BDD / Given-When-Then patterns
- You want test cases in QA Touch to display as Gherkin scenarios
- You're writing behavior-driven FlaUI tests with descriptive names
How It Works
- Parsing — Test results are parsed the same way as standard mode (NUnit3 XML or TRX)
- Module resolution — Modules are created/matched identically to standard mode
- Case creation — When a test case doesn't exist in QA Touch and
CreateCasesistrue:- Standard mode: Creates a case with pre-built steps via
POST /testCase - BDD mode: Creates a case with a Gherkin scenario via
POST /testCase/bdd
- Standard mode: Creates a case with pre-built steps via
- Result upload — Works identically in both modes
Generated Gherkin Format
For each test, the reporter generates a scenario from the test name:
Scenario: <test title>
GIVEN the application is launched and ready
WHEN I execute the test: <test title>
THEN it should complete successfully
For example, a test named GivenAppIsLaunched_WhenMainWindowAppears_ThenTitleIsVisible produces:
Scenario: GivenAppIsLaunched_WhenMainWindowAppears_ThenTitleIsVisible
GIVEN the application is launched and ready
WHEN I execute the test: GivenAppIsLaunched_WhenMainWindowAppears_ThenTitleIsVisible
THEN it should complete successfully
API Payload
The BDD endpoint receives:
| Field | Value |
|---|---|
projectKey |
Your project key |
sectionKey |
Module key (auto-resolved from test suite name) |
caseTitle |
Test name |
feature_script |
The generated Gherkin scenario |
description |
"BDD Scenario: <test title>" |
reference |
Full test name (namespace + class + method) |
Enable BDD Mode
CLI:
flaui-qatouch-upload --results results.xml --domain myco --token xxx \
--project PROJ --assign ukey --bdd
Library:
new FlaUIQATouchReporterOptions { BddMode = true, /* ... */ }
PowerShell script (if using the upload helper):
.\upload-to-qatouch.ps1 -Domain myco -Token xxx -ProjectKey PROJ -AssignTo ukey -Bdd
Note: BDD mode only affects newly created test cases. Existing cases in QA Touch are matched by title and left unchanged. Result uploads work the same way regardless of mode.
Project Structure
flaUIQATouchCSharp/
├── FlaUIQATouch.sln # Solution file
├── LICENSE # MIT License
├── README.md # This file
└── src/
└── FlaUIQATouch/ # Single NuGet package (net8.0)
├── FlaUIQATouch.csproj
├── Program.cs # CLI entry point (flaui-qatouch-upload)
├── QATouchClient.cs # HTTP client for QA Touch REST API
├── ResultParser.cs # NUnit3 XML & TRX parser
└── FlaUIQATouchReporter.cs # Main reporter (orchestrates everything)
CI/CD Examples
GitHub Actions
name: FlaUI Tests + QA Touch
on: [push, pull_request]
jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Run FlaUI tests
run: dotnet test --logger "nunit;LogFilePath=TestResults/results.xml"
- name: Install QA Touch CLI
run: dotnet tool install --global FlaUIQATouch
- name: Upload results to QA Touch
if: always()
run: |
flaui-qatouch-upload --results TestResults/results.xml `
--domain ${{ secrets.QATOUCH_DOMAIN }} `
--token ${{ secrets.QATOUCH_TOKEN }} `
--project ${{ secrets.QATOUCH_PROJECT }} `
--assign ${{ secrets.QATOUCH_ASSIGN }}
# Or use --bdd to create Gherkin scenarios in QA Touch:
# - name: Upload results to QA Touch (BDD)
# if: always()
# run: |
# flaui-qatouch-upload --results TestResults/results.xml `
# --domain ${{ secrets.QATOUCH_DOMAIN }} `
# --token ${{ secrets.QATOUCH_TOKEN }} `
# --project ${{ secrets.QATOUCH_PROJECT }} `
# --assign ${{ secrets.QATOUCH_ASSIGN }} `
# --bdd
Azure DevOps
trigger:
- main
pool:
vmImage: 'windows-latest'
steps:
- task: UseDotNet@2
inputs:
version: '8.0.x'
- script: dotnet test --logger "nunit;LogFilePath=TestResults/results.xml"
displayName: 'Run FlaUI tests'
- script: dotnet tool install --global FlaUIQATouch
displayName: 'Install QA Touch CLI'
- script: >
flaui-qatouch-upload
--results TestResults/results.xml
--domain $(QATOUCH_DOMAIN)
--token $(QATOUCH_TOKEN)
--project $(QATOUCH_PROJECT)
--assign $(QATOUCH_ASSIGN)
displayName: 'Upload results to QA Touch'
condition: always()
# Or use --bdd to create Gherkin scenarios in QA Touch:
# - script: >
# flaui-qatouch-upload
# --results TestResults/results.xml
# --domain $(QATOUCH_DOMAIN)
# --token $(QATOUCH_TOKEN)
# --project $(QATOUCH_PROJECT)
# --assign $(QATOUCH_ASSIGN)
# --bdd
# displayName: 'Upload results to QA Touch (BDD)'
# condition: always()
API Reference
FlaUIQATouchReporter
| Member | Description |
|---|---|
FlaUIQATouchReporter(FlaUIQATouchReporterOptions options) |
Creates a new reporter instance |
Task RunAsync() |
Parses results, resolves/creates QA Touch entities, and uploads results |
void Dispose() |
Disposes the underlying HTTP client |
ResultParser
| Member | Description |
|---|---|
static List<ParsedTest> ParseResultsFile(string filePath) |
Auto-detects NUnit3 XML or TRX and parses test results |
static string NormalizeTitle(string title) |
Normalizes a test name for matching (lowercase, alphanumeric + spaces) |
QATouchClient
| Member | Description |
|---|---|
Task<List<JsonElement>> GetTestCasesAsync(string? moduleKey) |
Retrieves test cases (paginated) |
Task CreateTestCaseAsync(...) |
Creates a test case with steps |
Task<JsonElement> CreateBddTestCaseAsync(...) |
Creates a BDD test case with a Gherkin feature_script via /testCase/bdd |
Task<List<JsonElement>> GetModulesAsync() |
Retrieves all modules (paginated) |
Task<JsonElement> CreateModuleAsync(string name, string? parentKey) |
Creates a module |
Task<List<JsonElement>> GetMilestonesAsync() |
Retrieves all milestones |
Task CreateMilestoneAsync(string name) |
Creates a milestone |
Task<JsonElement> CreateTestRunAsync(...) |
Creates a test run with specific cases |
Task<JsonElement> UpdateResultsAsync(string runKey, List<BulkResult> results) |
Bulk-updates test run results |
Task<JsonElement> AddResultWithCommentAsync(...) |
Adds a single result with optional comment and screenshot |
Troubleshooting
| Issue | Solution |
|---|---|
Unrecognised results format |
Ensure the file is valid NUnit3 XML (<test-run>) or TRX (<TestRun>) |
No test cases found |
Check that the results file contains <test-case> (NUnit) or <UnitTestResult> (TRX) elements |
API Error 401 |
Verify your ApiToken and Domain are correct |
API Error 404 |
Verify the ProjectKey exists in your QA Touch account |
Unable to resolve milestone key |
Check that your QA Touch plan supports milestones |
Unmapped: <test name> |
The test name didn't match any QA Touch case; enable CreateCases or check title normalization |
| CLI not found after install | Ensure ~/.dotnet/tools is in your PATH |
API Error 429 |
Rate-limited — the built-in retry (3 attempts, exponential backoff) should handle this automatically |
ArgumentException on startup |
A required option (Domain, ApiToken, ProjectKey) is empty or null |
Changelog
1.1.4
- Fix:
GetTestCasesAsync()pagination now uses deduplication instead ofper_page-based stop condition. The API returns variable item counts per page, which caused pagination to stop at page 1 even when more pages existed. - Fix:
moduleKeyquery parameter is silently ignored by the/getAllTestCasesAPI. Filtering by module is now done client-side usingitem.module_key.
License
MIT — see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.