Metamation Developer Notes

  • Developer Notes
  • Resource Map
  • Tools
    • Code Formatting
    • Configuring Git
    • Assignments
    • Testing with Windows Sandbox
    • Single Binary Applications
    • Self-Updating Applications
  • Flux Workflow
    • Flux Workflow
    • Flux Development Style Guide
    • Release notes for Flux
    • SonarCloud analysis for Flux
    • Build a Flux SDK Program
    • Shipping Flux
    • Flux.Test
      • BasicBendAuto Test
      • LaserAuto.cs Detailed Documentation
      • Laserexample:Test1
  • Technical Notes
    • File formats
      • .FX File Format
      • Au(Gold) streams
      • .GEO File Format
      • .LST File Format
      • .LST file - technology
      • .BNC File Format
      • .WAD Files: Flux Resource Packages
      • .Curl Files: ASCII config information
    • Folders App
      • Data\Bendtools
      • Data\Machines
    • FluxCode
      • Welcome & Repo Map
      • Architecture & System Context
      • Subsystem *Bend* (Press Brake)
      • Subsystem *BCell* (Bend Cell Automation)
      • API Maps
      • Data Model Catalog
      • Design Patterns -- A Reader's Guide
      • Tests and Golden Paths
      • Guided Code Tours
      • CAM Glossary with Code Mapping
      • Assignments
      • Flux Onboarding Handbook
    • Data flow
      • Part
        • Part.Dwg
        • Part.Load
        • Part.Load to Part.Dwg
      • BendTech.cs
      • Bend Technology
      • Laser Cutting Technology
    • LTS (Long term support mechanism)
    • Machine ID Scheme
  • FluxTester
    • Commands and Scripts
    • Command Cookbook
    • Script Commands
    • BendTooling.cs Test Inventory
    • LaserTooling.cs Test Steps
    • Fluxtester Recipe Book
    • Nesting.cs Overview
    • Nesting.cs Recipe Book
    • Job.cs Overview
    • Job.cs Recipe Book
  • Software license
  • 3d party software
    • Python
  • Documentation
    • Bento documentation
      • Structure of a Bento page
      • Attributes
    • Software
      • Software Requirement
      • Installation
      • Git workflow for documentation contributors
    • Quick start guide
    • Workflow
      • Roles and responsibilities
      • Analyze
      • Edit
      • Proofread
      • Pre-Publish
      • Publish
      • Release cycle
      • Information required for Documentation Team
      • Checklist for Documenters
    • Styleguide
    • Flavoring
    • Translation
      • Ordering translation for a new document
    • Citations
    • Annotation
FluxTester
/
Job.cs Recipe Book

Job.cs Recipe Book

Table of Contents

  1. Test Structure & Attributes

  2. IDisposable Pattern Setup

  3. File Operations

  4. Job Creation & Management

  5. Layout Operations

  6. Part Management

  7. Sheet Management

  8. Part Properties Configuration

  9. Dialog Interactions

  10. Add Dialog: Search & Sorting

  11. Keyboard & Input Commands

  12. Mouse Operations

  13. UI Element Access

  14. Data Extraction & Validation

  15. Script Execution

  16. Common Job Workflows

  17. Helper Methods Reference


Test Structure & Attributes

Basic Job Test Template

[TestFixture (1500, "Nesting Job Tests", Module = "FluxCut", Subsystem = "NestJob")]
internal class Job : IDisposable {
    const string TESTMACHINEID1 = "2230001";
    const string TESTMACHINEID2 = "97";
    const string FXFILE = "123.fx";

    [Test (ID, "Test Description", Type = Functional)]
    public void Test_CaseID () {
        // Test implementation
    }

    #region Helper Routines
    // Helper methods
    #endregion
}

Test Attributes

// TestFixture attribute
[TestFixture (1500, "Nesting Job Tests", Module = "FluxCut", Subsystem = "NestJob")]

// Test attribute with type
[Test (401, "Adding and deleting layouts", Type = Functional)]
[Test (402, "Moving layouts", Type = Functional)]
[Test (1528, "Selecting 'New Job' toolbar button opens a pop-up dialog", Type = Functional)]

// Test type options
Type = Functional

IDisposable Pattern Setup

Constructor & Dispose Pattern

// Constructor - Setup phase (runs before first test)
public Job () {
    Utils.ConfigureMachine (TESTMACHINEID1);  // Machine 2230001
    Utils.ConfigureMachine (TESTMACHINEID2);  // Machine 97
}

// Dispose - Cleanup phase (runs after all tests)
public void Dispose () {
    Utils.UninstallMachine (TESTMACHINEID1);
    Utils.UninstallMachine (TESTMACHINEID2);
    Utils.DeleteTempFile ();
}

Key Differences from FixtureInitialize/FixtureCleanUp: - Called per test instance (not per fixture) - More flexible resource management - Automatic cleanup via using statement

Machine Configuration

const string TESTMACHINEID1 = "2230001";  // Primary nesting machine
const string TESTMACHINEID2 = "97";       // Secondary laser machine

// Install machines
Utils.ConfigureMachine (TESTMACHINEID1);
Utils.ConfigureMachine (TESTMACHINEID2);

// Uninstall after tests
Utils.UninstallMachine (TESTMACHINEID1);
Utils.UninstallMachine (TESTMACHINEID2);

File Operations

Opening Job Files

// Open job file directly
Utils.OpenJobOrLayout (Utils.tData + "TwinJob.fxjob");
Utils.OpenJobOrLayout (Utils.tData + "NestingJobSample.fxjob");
Utils.OpenJobOrLayout (Utils.tData + "AsymmetricalPolygon.fxjob");

// Open job via helper
OpenJob ("NestingJobSample.fxjob");
OpenJob ("TwinJob.fxjob");

// Open job and Add dialog
OpenAddDialog ("NestingJobSample.fxjob");

File Access Paths

// Test data paths
Utils.tData                                      // Test data directory
Utils.tData + "TwinJob.fxjob"
Utils.tData + "NestingJobSample.fxjob"
Utils.tData + "AsymmetricalPolygon.fxjob"
Utils.tData + "Test_C150733/test.txt"
Utils.tData + "Test_C162420/test_ExportRemSheet.dxf"

// Temporary paths
Utils.tempPath                                   // Temp directory
Utils.tempPath + "test.dxf"
Utils.tempPath + "test.png"
Utils.tempPath + "temp.txt"

Cleanup Operations

// Delete temporary files
Utils.DeleteTempFile ();

// Delete specific file
File.Delete (Utils.tempPath + "test.dxf");

// Delete multiple files
foreach (var file in parts) File.Delete (Utils.tempPath + file);

Job Creation & Management

Creating New Job

// Create new job with empty layout
var job = CreateNewJob ();

// Create with job name
var job = CreateNewJob ("MyJob");

// Internal implementation
internal static AutomationElement? CreateNewJob (string name = "") {
    if (!Utils.IsRunning ()) Utils.OpenFlux ();
    var mainWin = Utils.MainWin;
    mainWin.FindFirstChild ("CmdBar").FindFirstChild (a => a.ByName ("new job")).Click ();
    var dlg = mainWin.FindFirstChild (a => a.ByName ("NewJobDlg"));
    if (dlg != null && string.IsNullOrEmpty (name)) Utils.Type (name);
    dlg?.FindFirstChild (a => a.ByName ("OK").And (a.ByControlType (ControlType.Button))).Click ();
    return Utils.FindElement (() => Utils.MainWin.FindFirstChild ("NewJobWindow"));
}

Opening Jobs

// Open Flux first
Utils.OpenFlux ();

// Open job
Utils.OpenJobOrLayout (Utils.tData + filename);

// Get job window
var job = Utils.MainWin.FindFirstChild ("NewJobWindow");
var newJobWindow = GetNewJobWindow ();  // With retry logic

Job Window Access

// Get main job window
var job = Utils.MainWin.FindFirstChild ("NewJobWindow");
var job = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("NewJobWindow"));

// Wait for job window to fully load
var newJobWindow = GetNewJobWindow ();  // Helper with retry

// Get job window parent
var jobWin = Utils.MainWin.FindFirstDescendant (a => a.ByName ("parts")).Parent;

Job Dialog Operations

// New Job dialog
Utils.MainWin.FindFirstChild ("CmdBar").FindFirstChild (a => a.ByName ("new job")).Click ();
var dlg = Utils.MainWin.FindFirstChild (a => a.ByName ("NewJobDlg"));

// Click OK button
dlg.FindFirstChild (a => a.ByName ("OK").And (a.ByControlType (ControlType.Button))).Click ();

// Click Cancel button
dlg.FindFirstChild (a => a.ByName ("Cancel").And (a.ByControlType (ControlType.Button))).Click ();

// Check if dialog exists
Assert.IsNotNull (dlg, "New job dialog is not found");

Layout Operations

Tab Navigation

// Switch to parts tab
job.FindFirstChild (a => a.ByName ("parts")).Click ();
Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("parts"))).Click ();

// Switch to layouts tab
job.FindFirstChild (a => a.ByName ("layouts")).Click ();

// Switch to sheets tab
job.FindFirstChild (a => a.ByName ("sheets")).Click ();

// Via keyboard shortcut
Utils.Type ('p');  // Parts view
Utils.Type ('l');  // Layouts view

Layout Management

// Access layout controls
var addBtn = job.FindFirstChild ("cAddLayout");
var removeBtn = job.FindFirstChild ("cRemovePattern");
var moveUpBtn = job.FindFirstChild ("cMoveUpBtn");
var moveDownBtn = job.FindFirstChild ("cMoveDownBtn");

// Add layout
addBtn.Click ();
job.FindFirstChild ("cAddLayout").Click ();

// Remove layout
removeBtn.Click ();
job.FindFirstChild ("cRemovePattern").Click ();

// Move layout up
moveUpBtn.Click ();
job.FindFirstChild ("cMoveUpBtn").Click ();

// Move layout down
moveDownBtn.Click ();
job.FindFirstChild ("cMoveDownBtn").Click ();

// Delete all layouts
for (int i = 0; i < 6; i++) removeBtn.Click ();

Layout List Operations

// Get patterns list
var patterns = job.FindFirstChild ("cPatternsList");

// Get all layouts
var layouts = patterns.FindAllChildren ();

// Get layout count
int count = patterns.FindAllChildren ().Length;

// Select layout by index
patterns.FindChildAt (index).Click ();

// Select random layout
patterns.FindChildAt (new Random ().Next (3)).Click ();

// Get first layout
patterns.FindFirstChild ();

// Get layouts via helper
var layouts = Utils.FindElements (() => newJobWindow.FindFirstChild ("cPatternsList").FindAllChildren ());

Nesting Operations

// Renest
job.FindFirstChild ("cNesting").Click ();

// Wait for nesting to complete
WaitTillNestingCompletes ();
WaitTillNestingCompletes (waitTime: 7);  // Custom wait time

// Helper implementation
internal static void WaitTillNestingCompletes (int waitTime = 6) {
    var newJobWin = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("NewJobWindow"));
    Utils.FindElement (() => newJobWin.FindFirstChild ("cPatternsList").FindFirstChild (),
        timeout: waitTime, timeOutMsg: "Nesting did not complete within timeout");
}

Layout Switching

// Switch to layout page (full workflow)
private static void SwitchToLayoutPage () {
    Utils.Type (ESC);
    var newJobWindow = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("NewJobWindow"));
    Utils.Type ('l');
    Utils.FindElement (() => newJobWindow.FindFirstChild ("cAddLayout"),
        timeOutMsg: "Layout page was not opened");
}

Layout Tree Dumping

// Dump automation tree for first layout
Utils.DumpAutomationElement (
    jobWin.FindFirstChild ("cPatternsList").FindFirstChild ()
);

// Compare with reference file
Assert.AreTextFilesEqual (
    Utils.tData + "Test_C150733/test.txt",
    Utils.tempPath + "temp.txt"
);

Part Management

Parts Tab Operations

// Switch to parts tab
job.FindFirstChild (a => a.ByName ("parts")).Click ();
Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("parts"))).Click ();

// Via keyboard
Utils.Type ('p');

Adding Parts

// Add parts to job
string[] parts = [FXFILE, "InnerContour.fx"];
AddPartsOrLayouts (ELoadOption.Part, parts);

// Add single part
AddPartsOrLayouts (ELoadOption.Part, new[] { "123.fx" });

// External module reference
using static FluxTester.NestingUtils;

Parts Table Access

// Get parts table
var partsTable = job.FindFirstChild (a => a.ByClassName ("PartsTable")).FindFirstChild ();

// Get parts table with scrollviewer
var partsTable = Utils.FindElement (
    () => job.FindFirstChild (a => a.ByClassName ("PartsTable")
        .And (a.ByControlType (ControlType.Custom)))
).FindFirstChild ();

// Get parts from scrollviewer
var scrollViewer = Utils.FindElement (
    () => job.FindFirstChild (a => a.ByClassName ("PartsTable"))
        .FindFirstChild (a => a.ByClassName ("ScrollViewer"))
);

Part Table Interactions

// Get all parts in table
var parts = partsTable.FindAllChildren ();

// Find part by name
partsTable.FindFirstChild (a => a.ByName ("123"));
partsTable.FindFirstChild (a => a.ByName (partName.Replace (".fx", "")));

// Find part by index
var idx = Array.FindIndex (parts, a => a.Name = "BigCircle");
partsTable.FindChildAt (idx);

// Get specific column in row
partsTable.FindChildAt (idx + 3);   // Priority column (idx + 3)
partsTable.FindChildAt (idx + 6);   // Group column (idx + 6)

Part Operations

// Select part
var part = partsTable.FindFirstChild (a => a.ByName ("Name"));
part.Click ();

// Select all parts
Utils.Type ('a', CONTROL);

// Delete part
job.FindFirstChild ("cPartRemoveBtn").Click ();

// Delete all parts
foreach (var part in parts.Reverse ()) {
    job.FindFirstChild ("cPartRemoveBtn").Click ();
}

// Save parts
job.FindFirstChild ("cSavePart").Click ();

Part Quantity Management

// Get quantity indices
var list = partsTable.FindAllChildren ();
var indices = list.Select ((el, index) => new { el, idx = index })
    .Where (x => x.el.ClassName = "CheckBox")
    .Select (x => x.idx)
    .ToList ();

// Update quantity for each part
for (int i = 0; i < parts.Length + 1; i++) {
    var pt = partsTable.FindChildAt (indices[i] - 5).GetClickablePoint ();
    Utils.MouseClick (new System.Drawing.Point (pt.X - 40, pt.Y));
    Thread.Sleep (500);
    Utils.MouseClick (Mouse.Position, doubleClick: true);
    Utils.Type ($"{(i + 1) * 10}");  // 10, 20, 30, etc.
}

Material Warning Handling

// Check for warning image
var warning = scrollViewer.FindFirstChild (a => a.ByClassName ("Image"));

if (warning != null) {
    warning.Click ();
    Utils.MainWin.FindFirstChild ()
        .FindFirstChild (a => a.ByClassName ("ScrollViewer"))
        .FindFirstChild (a => a.ByName ("Ignore and convert"))?.Click ();
}

// Alternative: Ignore and convert all
main.FindFirstDescendant (a => a.ByName ("Ignore and convert all"))?.Click ();

Part Validation

// Check part count in layout
CheckPartsCountInLayout (expectedPartsCount: 5);
CheckPartsCountInLayout (60, layoutIdx: 0);

// Helper
public static void CheckPartsCountInLayout (int expected, int layoutIdx = 0) {
    Utils.MainWin.FindFirstChild ("NewJobWindow")
        .FindFirstChild ("cPatternsList")
        .FindChildAt (layoutIdx)
        .FindFirstChild (a => a.ByClassName ("Image"))
        .Click ();
    Assert.AreEqual (
        Utils.RunScriptWithOutput ("var result = Flux.Layout.Current.PartCount.ToString();"),
        expected.ToString ()
    );
}

Sheet Management

Sheets Tab Operations

// Switch to sheets tab
job.FindFirstChild (a => a.ByName ("sheets")).Click ();

// Access sheet buttons
var addBtn = job.FindFirstChild ("cAddSheet");
var delBtn = job.FindFirstChild ("cDelSheet");
var importBtn = job.FindFirstChild ("cImportSheet");

// Add sheets
for (int i = 0; i < 5; i++) addBtn.Click ();

// Delete sheets
for (int i = 0; i < 6; i++) delBtn.Click ();

Sheets Table Access

// Get sheets table
var pane = job.FindFirstChild (a => a.ByClassName ("SheetsTable")).FindFirstChild ();

// Find sheets by size
var sheets = pane.FindAllChildren (a => a.ByName ("3000x1500"));

// Count sheets
int sheetCount = sheets.Length;

// Verify sheet was added/removed
Assert.Equals (pane.FindAllChildren (a => a.ByName ("3000x1500")).Length, 6);

Sheet Import/Export

// Export remainder sheet
g.FindFirstChild (a => a.ByName ("Layout")).Click ();
g.FindFirstDescendant (a => a.ByName ("Export Remainder Sheets...")).Click ();

// Save exported file
g.FindFirstDescendant (a => a.ByClassName ("Button").And (a.ByName ("Save"))).Click ();

// Import sheet
job.FindFirstChild ("cImportSheet").Click ();
Thread.Sleep (1000);

// Type file path
Utils.Type ($"{Utils.tData}Test_C162420/test_ExportRemSheet.dxf".Replace ('/', '\\'));

// Click Open
Utils.MainWin.FindFirstChild ()
    .FindFirstChild (a => a.ByClassName ("Button").And (a.ByName ("Open")))
    .Click ();

Sheet Operations

// Create remainder sheet
SkeletonCut.CreateRemainderSheet ();

// Click on sheet
Utils.MouseClick (Utils.MeasurePtToPixel (new Utils.Measure (15.5, 15.5)));

// Get dialog
var g = Utils.WaitForGlob ();

// Delete sheet with confirmation
job.FindFirstChild ("cDelSheet").Click ();
Utils.MainWin.FindFirstChild ()
    .FindFirstChild (a => a.ByClassName ("Button").And (a.ByName ("Yes")))?.Click ();

Part Properties Configuration

Priority Settings

// Set part priority
string[] parts = ["BigCircle", "Rectangle(400x200)", "Square(250x250)"];
List<string[]> priorityCombinations = [
    ["1", "2", "3"],
    ["2", "1", "3"],
    ["3", "2", "1"],
    ["0", "1", "2"]
];

// For each part, set priority
for (int k = 0; k < 3; k++) {
    var idx = Array.FindIndex (partsTable.FindAllChildren (), a => a.Name = parts[k]);
    partsTable.FindChildAt (idx + 3).DoubleClick ();  // Priority column
    Utils.MouseClick (Mouse.Position, doubleClick: true);
    Utils.Type ($"{priorityCombinations[j][k]}");     // Enter priority
}

// Renest with new priorities
newJobWindow.FindFirstChild ("cNesting").Click ();
var layouts = Utils.FindElements (() => newJobWindow.FindFirstChild ("cPatternsList").FindAllChildren ());

// Validate each layout
for (int i = 0; i < layouts.Length; i++) {
    layouts[i].Click ();
    Nesting.CheckPartsInLayout ("C122180", $"test_SetPriority{j + 1}-{i + 1}");
}

Rotation Settings

// Set part rotation (6 options available)
for (int i = 0; i < 6; i++) {
    newJobWindow.FindFirstChild (a => a.ByName ("parts")).Click ();

    // Find and click rotation column
    newJobWindow.FindFirstChild (a => a.ByClassName ("PartsTable"))
        .FindFirstChild (a => a.ByClassName ("ScrollViewer"))
        .FindAllChildren (a => a.ByClassName ("TextBlock"))[15]
        .Click ();

    // Select rotation option
    newJobWindow.FindFirstDescendant (a => a.ByClassName ("ContextMenu"))
        .AsMenu ()
        .Items[i]
        .AsMenuItem ()
        .Click ();

    // Switch to layout and validate
    SwitchToLayoutPage ();
    Nesting.CheckPartsInLayout ("C122181", $"test_SetRotation{i}");
}

Grouping Settings

// Set part group
string[] parts = ["BigCircle", "Rectangle(400x200)", "Square(250x250)"];
List<string[]> groupCombinations = [
    ["1", "2", "3"],
    ["2", "1", "3"],
    ["3", "2", "1"],
    ["0", "1", "2"]
];

// For each part, set group
for (int k = 0; k < 3; k++) {
    var idx = Array.FindIndex (partsTable.FindAllChildren (), a => a.Name = parts[k]);
    partsTable.FindChildAt (idx + 6).DoubleClick ();  // Group column (idx + 6)
    Utils.MouseClick (Mouse.Position, doubleClick: true);
    Utils.Type ($"{groupCombinations[j][k]}");         // Enter group ID
}

// Renest and validate layouts
var layouts = Utils.FindElements (() => newJobWindow.FindFirstChild ("cPatternsList").FindAllChildren ());
for (int i = 0; i < layouts.Length; i++) {
    layouts[i].Click ();
    Nesting.CheckPartsInLayout ("C122182", $"test_LayoutGroup{j + 1}-{i + 1}");
}

Dialog Interactions

New Job Dialog

// Open new job dialog
Utils.MainWin.FindFirstChild ("CmdBar").FindFirstChild (a => a.ByName ("new job")).Click ();

// Get dialog
var dlg = Utils.MainWin.FindFirstChild (a => a.ByName ("NewJobDlg"));

// Type job name
if (dlg != null && string.IsNullOrEmpty (name)) Utils.Type (name);

// Click OK
dlg?.FindFirstChild (a => a.ByName ("OK").And (a.ByControlType (ControlType.Button))).Click ();

// Click Cancel
dlg.FindFirstChild (a => a.ByName ("Cancel").And (a.ByControlType (ControlType.Button))).Click ();

// Verify dialog appears
Assert.IsNotNull (dlg, "New job dialog is not found");

Save Parts Dialog

// Open save dialog
job.FindFirstChild ("cSavePart").Click ();

// Find dialog
Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("Select Folder")));

// Type folder path
Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("Select Folder")))
    .FindFirstChild (a => a.ByControlType (ControlType.Edit).And (a.ByName ("Folder:")))
    .Click ();
Utils.Type (Utils.tempPath.Replace ('/', '\\')[..^1]);

// Click Save
Utils.FindElement (() => Utils.MainWin.FindFirstDescendant (a => a.ByName ("Select Folder").And (a.ByClassName ("Button"))))
    .Click ();

Confirmation Dialogs

// Click Yes button
Utils.MainWin.FindFirstChild ()
    .FindFirstChild (a => a.ByClassName ("Button").And (a.ByName ("Yes")))
    ?.Click ();

// Click No button
Utils.MainWin.FindFirstChild ()
    .FindFirstChild (a => a.ByClassName ("Button").And (a.ByName ("No")))
    ?.Click ();

// Material conversion warning
Utils.MainWin.FindFirstDescendant (a => a.ByName ("Ignore and convert all"))
    ?.Click ();

Save As Dialog

// Wait for Save As dialog
Utils.FindElement (() => Utils.MainWin.FindFirstDescendant (a => a.ByName ("Save As")));

// Click Save button
g.FindFirstDescendant (a => a.ByClassName ("Button").And (a.ByName ("Save")))
    .Click ();

// Wait for file creation
Utils.WaitForFileExists (tempfile);

Add Dialog: Search & Sorting

Add Dialog Access

// Open Add dialog
Utils.Type ('a');

// Open Add dialog via helper
OpenAddDialog ("NestingJobSample.fxjob");

// Helper implementation
static void OpenAddDialog (string fileName) {
    OpenJob (fileName);
    Utils.Type (ESC);
    Utils.Type ('a');
}

Search Operations

// Get search box
var searchBox = GetSearchBox ();
var searchBoxQuery = searchBox.FindFirstChild (a => a.ByClassName ("TextBox")).AsTextBox ();

// Search by part name
foreach (var uniquePartName in partNames.Distinct ()) {
    searchBoxQuery.Text = uniquePartName;
    var addPartNames = ExtractPartNames (GetParts ());
    Assert.True (addPartNames.All (addPartName => addPartName = uniquePartName));
}

// Case-insensitive search
searchBoxQuery.Text = uniquePartName.ToUpper ();
var uppercaseResults = GetParts ();
searchBoxQuery.Text = uniquePartName.ToLower ();
var lowercaseResults = GetParts ();
Assert.AreEqual (ExtractPartNames (uppercaseResults), ExtractPartNames (lowercaseResults));

// Empty search (shows all)
searchBoxQuery.Text = "";
var addPartNames1 = ExtractPartNames (GetParts ());

// Invalid search
searchBoxQuery.Text = "ahsk%$$^%^&";
Assert.True (GetParts ().Length = 0);

// Placeholder text
searchBoxQuery.Text = "Search Parts";
var parts = GetParts ();

Sort Operations

// Get sort by combobox
var comboBox = GetComboBox ();

// Sort by Name
comboBox.Select ("Name");
SortAscendingly (true);
bool ascSort = IsSorted (ExtractPartNames (GetParts ()));

// Sort by Area
comboBox.Select ("Area");
var widths = GetMeasurements (false);
var heights = GetMeasurements ();
var areas = [.. widths.Select ((w, i) => w * heights[i])];

// Sort by Width
comboBox.Select ("Width");
var ascSort = IsSorted (GetMeasurements (false));

// Sort by Height
comboBox.Select ("Height");
var ascSort = IsSorted (GetMeasurements ());

// Sort by Remaining Quantity
comboBox.Select ("Remaining Quantity");
var ascSort = !IsSorted (GetRemQuantities ());

Sort Direction

// Toggle ascending/descending
SortAscendingly (true);   // Ascending
SortAscendingly (false);  // Descending

// Helper
static void SortAscendingly (bool b) {
    GetSortToggleCheckBox ().IsToggled = b;
}

// Get toggle checkbox
var toggleCheckBox = Utils.MainWin.FindFirstDescendant (a => a.ByName ("Ascending")).AsCheckBox ();

Add Dialog Parts List

// Get all parts
var parts = GetParts ();

// Extract part names
string[] partNames = ExtractPartNames (parts);

// Get part measurements
float[] widths = GetMeasurements (false);
float[] heights = GetMeasurements ();

// Get remaining quantities
int[] remQuantities = GetRemQuantities ();

// Helper: Extract part names
static string[] ExtractPartNames (AutomationElement[] parts) =>
    [.. parts.Select (a => a.Name.Split ('\n', 2).First ()[3..])];

// Helper: Get measurements
static float[] GetMeasurements (bool height = true) {
    var parts = GetParts ();
    int len = parts.Length;
    var measurements = new float[len];
    for (int i = 0; i < len; i++) {
        var dims = parts[i].Name.Split ('\n', 3)[1].Split ('x');
        measurements[i] = float.Parse (height ? dims[1] : dims[0]);
    }
    return measurements;
}

// Helper: Get remaining quantities
static int[] GetRemQuantities () {
    var parts = GetParts ();
    int len = parts.Length;
    int[] remQuantities = new int[len];
    for (int i = 0; i < parts.Length; i++) {
        var line3 = parts[i].Name.Split ('\n', 4)[2];
        int idx = line3.IndexOfAny (['-', '+']);
        remQuantities[i] = idx >= 0 ? int.Parse (line3[idx..]) : 0;
    }
    return remQuantities;
}

Add Dialog UI Validation

// Check search box exists
Assert.IsNotNull (GetSearchBox (), "Search Box not found");

// Check sort option exists
Assert.IsNotNull (mainWin.FindFirstDescendant (a => a.ByName ("Ascending")), "Sort option not found");

// Check sort options available
var comboBoxItems = GetComboBox ().Items;
foreach (var item in comboBoxItems) {
    Assert.IsNotNull (item, item.Name + " element not found");
}

Keyboard & Input Commands

Navigation Keys

// Tab navigation
Utils.Type ('p');              // Parts tab
Utils.Type ('l');              // Layouts tab
Utils.Type ('a');              // Add dialog

// Escape/Close
Utils.Type (ESC);
Utils.Type (ESCAPE);

// Confirm
Utils.Type (ENTER);

// Movement
Utils.Type (LEFT);
Utils.Type (RIGHT);
Utils.Type (DELETE);

Modifier Keys

using static FluxTester.Utils.KeyShort;

// Ctrl combinations
Utils.Type ('a', CONTROL);     // Select all
Utils.Type ('c', CONTROL);     // Copy
Utils.Type ('x', CONTROL);     // Cut
Utils.Type ('v', CONTROL);     // Paste

// Key constants
CONTROL
SHIFT
ESC
ESCAPE
ENTER
TAB
LEFT
RIGHT
DELETE

Text Input

// Type job name
Utils.Type (name);

// Type priority value
Utils.Type ($"{priority}");

// Type quantity
Utils.Type ($"{(i + 1) * 10}");

// Type file path
Utils.Type ($"{Utils.tData}Test_C162420/test_ExportRemSheet.dxf".Replace ('/', '\\'));

// Type folder path
Utils.Type (Utils.tempPath.Replace ('/', '\\')[..^1]);

Mouse Operations

Basic Mouse Actions

// Single click
Utils.MouseClick (element.GetClickablePoint ());

// Double click
Utils.MouseClick (pt, doubleClick: true);

// Click with modifier
Utils.MouseClick (pt, CONTROL);    // Ctrl+click

// Get current position
var currentPos = Mouse.Position;

Point Conversion

// Convert measurement to pixels
var pt = Utils.MeasurePtToPixel (new Utils.Measure (15.5, 15.5));
var pt = Utils.MeasurePtToPixel (new Utils.Measure (50, 90));

// Adjust point coordinates
new System.Drawing.Point (pt.X - 40, pt.Y);

Drag & Drop

// Standard drag and drop
Utils.DragAndDrop (startPoint, endPoint);

// Copy (Ctrl+drag)
using (var ob = Keyboard.Pressing ((VirtualKeyShort)CONTROL))
    Utils.DragAndDrop (start, end);

// Twinline (Shift+drag)
using (var ob = Keyboard.Pressing ((VirtualKeyShort)SHIFT))
    Utils.DragAndDrop (start, end);

UI Element Access

Job Window Navigation

// Get main window
var mainWin = Utils.MainWin;

// Get job window
var job = Utils.MainWin.FindFirstChild ("NewJobWindow");

// Get job window with retry
var newJobWindow = GetNewJobWindow ();

// Helper
static AutomationElement GetNewJobWindow () {
    AutomationElement newJobWindow;
    do newJobWindow = Utils.MainWin.FindFirstChild ("NewJobWindow");
    while (newJobWindow.FindAllChildren ().Length = 0);
    return newJobWindow;
}

Tab Controls

// Find and click tabs
job.FindFirstChild (a => a.ByName ("parts")).Click ();
job.FindFirstChild (a => a.ByName ("layouts")).Click ();
job.FindFirstChild (a => a.ByName ("sheets")).Click ();

// Verify tab exists
Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("parts")));

Button Controls

// Find buttons by automation ID
var addBtn = job.FindFirstChild ("cAddLayout");
var removeBtn = job.FindFirstChild ("cRemovePattern");
var nestBtn = job.FindFirstChild ("cNesting");
var savePartBtn = job.FindFirstChild ("cSavePart");
var importSheetBtn = job.FindFirstChild ("cImportSheet");

// Find buttons by name
Utils.MainWin.FindFirstChild ("CmdBar").FindFirstChild (a => a.ByName ("new job"));

// Click button
button.Click ();

Table Controls

// Get parts table
var partsTable = job.FindFirstChild (a => a.ByClassName ("PartsTable")).FindFirstChild ();

// Get sheets table
var sheetsTable = job.FindFirstChild (a => a.ByClassName ("SheetsTable")).FindFirstChild ();

// Get patterns list
var patterns = job.FindFirstChild ("cPatternsList");

// Navigate table
partsTable.FindChildAt (index);
patterns.FindChildAt (0);
patterns.FindAllChildren ();

ComboBox Controls

// Get sort by combobox
var comboBox = Utils.FindElement (
    () => Utils.MainWin.FindFirstDescendant (a => a.ByControlType (ControlType.ComboBox))
).AsComboBox ();

// Select option
comboBox.Select ("Name");
comboBox.Select ("Area");
comboBox.Select ("Width");
comboBox.Select ("Height");
comboBox.Select ("Remaining Quantity");

// Expand/collapse
comboBox.Expand ();
comboBox.Items[index].Click ();

// Get items
var items = comboBox.Items;

CheckBox Controls

// Get sort toggle
var toggleCheckBox = Utils.MainWin.FindFirstDescendant (a => a.ByName ("Ascending")).AsCheckBox ();

// Toggle state
toggleCheckBox.IsToggled = true;
toggleCheckBox.IsToggled = false;

TextBox Controls

// Get textbox
var searchBox = GetSearchBox ();
var textBox = searchBox.FindFirstChild (a => a.ByClassName ("TextBox")).AsTextBox ();

// Set text
textBox.Text = "searchValue";

// Get text
string value = textBox.Text;

Glob/Dialog Controls

// Wait for glob
var g = Utils.WaitForGlob ();

// Find in glob
g.FindFirstChild (a => a.ByName ("Layout"));
g.FindFirstDescendant (a => a.ByName ("Export Remainder Sheets..."));

// Get context menu
var menu = newJobWindow.FindFirstDescendant (a => a.ByClassName ("ContextMenu")).AsMenu ();
menu.Items[i].AsMenuItem ().Click ();

Data Extraction & Validation

Extracting Job Data

// Get part names from job
List<string> commands = [
    "StringBuilder sb = new ();",
    "foreach (var part in Flux.JobEditor.Current.Job.Parts) sb.Append(part.Name + \",\");",
    "var result = sb.ToString();"
];
string[] partNames = Utils.RunScriptWithOutput (commands).Split (',', options: StringSplitOptions.RemoveEmptyEntries);

Part Name Extraction

// Extract from Add dialog
string[] ExtractPartNames (AutomationElement[] parts) =>
    [.. parts.Select (a => a.Name.Split ('\n', 2).First ()[3..])];

// Usage
var addPartNames = ExtractPartNames (GetParts ());

Sorting Validation

// Check if sorted ascending
IsSorted (array);
IsSorted (array, ascending: true);

// Check if sorted descending
IsSorted (array, ascending: false);

// Generic implementation
static bool IsSorted<T> (T[] array, bool ascending = true) where T : IComparable<T> =>
    array.SequenceEqual (ascending ? array.OrderBy (x => x) : array.OrderByDescending (x => x));

// Usage
bool namesSorted = IsSorted (ExtractPartNames (GetParts ()));
bool areasSorted = IsSorted (GetAreas ());

Layout Data Dumping

// Dump to file
Utils.DumpAutomationElement (
    jobWin.FindFirstChild ("cPatternsList").FindFirstChild ()
);

// Verify with reference
Assert.AreTextFilesEqual (
    Utils.tData + "Test_C150733/test.txt",
    Utils.tempPath + "temp.txt"
);

File Validation

// Check file exists
Utils.WaitForFileExists (file);
Assert.True (File.Exists (file));

// Compare text files
Assert.AreTextFilesEqual (expectedPath, actualPath);

// Compare binary files (DXF)
Assert.AreTextFilesEqual (
    $"{Utils.tData}Test_C162420/test_ExportRemSheet.dxf",
    tempfile
);

Script Execution

Job Data Queries

// Get all parts in job
List<string> commands = [
    "StringBuilder sb = new ();",
    "foreach (var part in Flux.JobEditor.Current.Job.Parts) sb.Append(part.Name + \",\");",
    "var result = sb.ToString();"
];
string result = Utils.RunScriptWithOutput (commands);

Layout Validation

// Get part count in current layout
Utils.RunScriptWithOutput ("var result = Flux.Layout.Current.PartCount.ToString();");

// Validate parts in layout
CheckPartsCountInLayout (expectedCount);
CheckPartsCountInLayout (5, layoutIdx: 0);

Layout Properties

// Check if all parts within margin
Utils.RunScriptWithOutput ("var result = Flux.Layout.Current.Children.All (...).ToString();");

// Access layout children
Nesting.AreAllPartsNestedWithinSheet (includeTooling: true);

Multi-Line Script

List<string> commands = [
    "var lay = Flux.Layout.Current;",
    "var sm = lay.SheetMargin;",
    "var size = lay.SheetSize;",
    "var bound = new Flux.Bound2 (sm.Left - 0.1, sm.Bottom - 0.1, size.X - sm.Right + 0.1, size.Y - sm.Top + 0.1);",
    "var result = lay.Children.All (a => bound.Contains (a.GetBoundSansTooling ())).ToString();"
];

Common Job Workflows

Basic Job Creation Workflow

[Test (1528, "Selecting 'New Job' toolbar button opens a pop-up dialog")]
public void Test_C122130 () {
    // 1. Open Flux
    Utils.OpenFlux ();

    // 2. Open New Job dialog
    Utils.MainWin.FindFirstChild ("CmdBar").FindFirstChild (a => a.ByName ("new job")).Click ();

    // 3. Verify dialog appears
    Assert.IsNotNull (Utils.MainWin.FindFirstChild (a => a.ByName ("NewJobDlg")),
        "New job dialog is not found");
}

Job with Parts Addition Workflow

[Test (1530, "Save Parts(s) from the job")]
public void Test_C200408 () {
    // 1. Open Flux and create job
    Utils.OpenFlux ();
    var job = CreateNewJob ();

    // 2. Add parts
    string[] parts = [FXFILE, "InnerContour.fx"];
    AddPartsOrLayouts (ELoadOption.Part, parts);

    // 3. Switch to parts tab
    Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("parts"))).Click ();

    // 4. Select all and save
    job.FindFirstChild (a => a.ByClassName ("PartsTable")).FindFirstChild ()
        .FindFirstChild (a => a.ByName ("Name")).Click ();
    Utils.Type ('a', CONTROL);
    job.FindFirstChild ("cSavePart").Click ();

    // 5. Specify save location
    Utils.FindElement (() => job.FindFirstChild (a => a.ByName ("Select Folder")))
        .FindFirstChild (a => a.ByControlType (ControlType.Edit).And (a.ByName ("Folder:")))
        .Click ();
    Utils.Type (Utils.tempPath.Replace ('/', '\\')[..^1]);

    // 6. Save and verify
    Utils.FindElement (() => Utils.MainWin.FindFirstDescendant (a => a.ByName ("Select Folder")
        .And (a.ByClassName ("Button")))).Click ();

    foreach (var filename in parts) {
        var file = Utils.tempPath + filename;
        Utils.WaitForFileExists (file);
        Assert.True (File.Exists (file));
        Thread.Sleep (500);
    }
}

Layout Management Workflow

[Test (401, "Adding and deleting layouts")]
public void Test_C150732 () {
    // 1. Open job
    Utils.OpenJobOrLayout (Utils.tData + "TwinJob.fxjob");

    // 2. Get job window and switch to layouts
    var jobWin = Utils.MainWin.FindFirstDescendant (a => a.ByName ("parts")).Parent;
    jobWin.FindFirstChild (a => a.ByName ("layouts")).Click ();

    // 3. Get buttons
    var addBtn = jobWin.FindFirstChild ("cAddLayout");
    var removeBtn = jobWin.FindFirstChild ("cRemovePattern");

    // 4. Add layouts
    Assert.True (layoutsCount () = 1);
    for (int i = 0; i < 6; i++) addBtn.Click ();
    Assert.True (layoutsCount () = 7);

    // 5. Remove layouts
    for (int i = 0; i < 3; i++) removeBtn.Click ();
    Assert.True (layoutsCount () = 4);

    // 6. Remove middle layout
    jobWin.FindFirstChild ("cPatternsList").FindChildAt (new Random ().Next (3)).Click ();
    removeBtn.Click ();
    Assert.True (layoutsCount () = 3);

    // Helper
    int layoutsCount () => jobWin.FindFirstChild ("cPatternsList").FindAllChildren ().Length;
}

Priority Setting Workflow

[Test (3527, "Set the priority for the part")]
public void Test_C122180 () {
    // 1. Open job
    Utils.OpenJobOrLayout (Utils.tData + "NestingJobSample.fxjob");

    // 2. Test multiple priority combinations
    string[] parts = ["BigCircle", "Rectangle(400x200)", "Square(250x250)"];
    List<string[]> priorityCombinations = [["1", "2", "3"], ["2", "1", "3"], ["3", "2", "1"], ["0", "1", "2"]];

    for (int j = 0; j < priorityCombinations.Count; j++) {
        var newJobWindow = GetNewJobWindow ();
        newJobWindow.FindFirstChild (a => a.ByName ("parts")).Click ();

        // 3. Get parts table
        var partsTable = Utils.FindElement (() => newJobWindow
            .FindFirstChild (a => a.ByClassName ("PartsTable")
            .And (a.ByControlType (ControlType.Custom)))).FindFirstChild ();

        // 4. Set priority for each part
        for (int k = 0; k < 3; k++) {
            var idx = Array.FindIndex (partsTable.FindAllChildren (), a => a.Name = parts[k]);
            partsTable.FindChildAt (idx + 3).DoubleClick ();
            Utils.MouseClick (Mouse.Position, doubleClick: true);
            Utils.Type ($"{priorityCombinations[j][k]}");
        }

        // 5. Switch to layout and renest
        SwitchToLayoutPage ();
        Utils.FindElement (() => newJobWindow.FindFirstChild ("cPatternsList").FindFirstChild ()).Click ();
        Utils.Type ("a", CONTROL);
        Utils.Type (DELETE);
        newJobWindow.FindFirstChild ("cNesting").Click ();

        // 6. Validate layouts
        var layouts = Utils.FindElements (() => newJobWindow.FindFirstChild ("cPatternsList").FindAllChildren ());
        for (int i = 0; i < layouts.Length; i++) {
            layouts[i].Click ();
            Nesting.CheckPartsInLayout ("C122180", $"test_SetPriority{j + 1}-{i + 1}");
        }
    }
}

Search & Sort Workflow

[Test (3531, "Search the parts by its name in add button")]
public void Test_C200439 () {
    // 1. Open job
    OpenJob ("NestingJobSample.fxjob");

    // 2. Get job window and parts
    var mainWin = Utils.MainWin;
    var newJobWindow = mainWin.FindFirstChild ("NewJobWindow");
    newJobWindow.FindFirstChild (a => a.ByName ("parts")).Click ();

    // 3. Get all part names
    List<string> commands = [
        "StringBuilder sb = new ();",
        "foreach (var part in Flux.JobEditor.Current.Job.Parts) sb.Append(part.Name + \",\");",
        "var result = sb.ToString();"
    ];
    string[] partNames = Utils.RunScriptWithOutput (commands)
        .Split (',', options: StringSplitOptions.RemoveEmptyEntries);

    // 4. Open Add dialog
    Utils.Type (ESC);
    Utils.Type ('a');
    var searchBoxQuery = GetSearchBox ()
        .FindFirstChild (a => a.ByClassName ("TextBox")).AsTextBox ();

    // 5. Search for each unique part
    foreach (var uniquePartName in partNames.Distinct ()) {
        searchBoxQuery.Text = uniquePartName;
        var addPartNames = ExtractPartNames (GetParts ());
        Assert.True (addPartNames.All (addPartName => addPartName = uniquePartName));
    }

    // 6. Test case-insensitive search
    foreach (var uniquePartName in partNames.Distinct ()) {
        searchBoxQuery.Text = uniquePartName.ToUpper ();
        var uppercaseResults = GetParts ();
        searchBoxQuery.Text = uniquePartName.ToLower ();
        var lowercaseResults = GetParts ();
        Assert.AreEqual (ExtractPartNames (uppercaseResults), ExtractPartNames (lowercaseResults));
    }

    // 7. Test empty search and invalid search
    searchBoxQuery.Text = "";
    var addPartNames1 = ExtractPartNames (GetParts ());
    Assert.True (partNames.All (partName => addPartNames1.Contains (partName)));

    searchBoxQuery.Text = "ahsk%$$^%^&";
    Assert.True (GetParts ().Length = 0);
}

Copy/Paste Workflow

[Test (3530, "Cut/Copy & Paste parts in layout sheets")]
public void C145731 () {
    // 1. Open job
    Utils.OpenJobOrLayout (Utils.tData + "AsymmetricalPolygon.fxjob");

    // 2. Handle material warning if needed
    var newJobWindow = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("NewJobWindow"));
    newJobWindow.FindFirstChild (a => a.ByName ("parts")).Click ();
    var scrollViewer = newJobWindow.FindFirstChild (a => a.ByClassName ("PartsTable"))
        .FindFirstChild (a => a.ByClassName ("ScrollViewer"));
    var warning = scrollViewer.FindFirstChild (a => a.ByClassName ("Image"));
    if (warning != null) {
        warning.Click ();
        Utils.MainWin.FindFirstChild ()
            .FindFirstChild (a => a.ByClassName ("ScrollViewer"))
            .FindFirstChild (a => a.ByName ("Ignore and convert"))?.Click ();
    }

    // 3. Test Copy/Paste
    SwitchToLayoutPage ();
    Thread.Sleep (2000);
    Utils.Type (ESC);
    var pt = Utils.MeasurePtToPixel (new Utils.Measure (50, 90));
    Utils.MouseClick (pt);
    Utils.Type ("c", CONTROL);    // Copy
    Utils.Type ("v", CONTROL);    // Paste
    Assert.True (Nesting.AreAllPartsNestedWithinSheet ());
    Nesting.CheckPartsInLayout ("C145731", "test_CopyPaste");

    // 4. Test Cut/Paste
    SwitchToLayoutPage ();
    newJobWindow.FindFirstChild ("cRemovePattern").Click ();
    newJobWindow.FindFirstChild ("cNesting").Click ();
    Thread.Sleep (2000);
    Utils.Type (ESC);
    Thread.Sleep (500);
    Utils.MouseClick (pt);
    Utils.Type ("x", CONTROL);    // Cut
    Utils.Type ("v", CONTROL);    // Paste
    Assert.True (Nesting.AreAllPartsNestedWithinSheet ());
    Nesting.CheckPartsInLayout ("C145731", "test_CutPaste");
}

Helper Methods Reference

Job Operations

CreateNewJob(string name = "")              // Create new job
OpenJob(string fileName)                    // Open job from test data
GetNewJobWindow()                           // Get job window with retry
OpenAddDialog(string fileName)              // Open job and Add dialog

Layout Operations

SwitchToLayoutPage()                        // Navigate to layout tab
WaitTillNestingCompletes(int waitTime = 6) // Wait for nesting
CheckPartsCountInLayout(int expected, int layoutIdx = 0)  // Validate part count

Search & Sort

GetComboBox()                               // Get Sort By dropdown
GetSearchBox()                              // Get search textbox
GetSortToggleCheckBox()                     // Get sort direction toggle
GetParts()                                  // Get all parts in Add dialog
GetNewJobWindow()                           // Get job window after loading

Data Extraction

ExtractPartNames(AutomationElement[] parts)      // Extract names from parts
GetMeasurements(bool height = true)               // Extract width/height values
GetRemQuantities()                                // Extract remaining quantities
GetAreas()                                        // Calculate areas (width × height)

Sorting & Comparison

IsSorted<T>(T[] array, bool ascending = true)     // Check sort order
SortAscendingly(bool b)                           // Toggle sort direction

Best Practices

✅ DO: - Use GetNewJobWindow() for consistent job window access with retry logic - Always call Utils.Type (ESC) to finalize operations and close dialogs - Use IDisposable pattern for comprehensive cleanup - Use measurement points for consistent coordinate conversion - Validate with both file comparison AND part count checking - Use meaningful variable names for table indices and part references - Add comments for non-obvious UI automation steps - Use helper methods for repeated patterns (search, sort, priority) - Wait appropriately for async operations (nesting, file operations)

❌ DON’T: - Hardcode pixel coordinates (always use measurement point conversion) - Skip Utils.WaitForFileExists() when checking file creation - Mix view switches without proper verification - Create multiple jobs without adequate cleanup - Skip assertions in validation workflows - Forget to handle material conversion warnings - Use generic variable names (g, dlg, etc. without context) - Access table children without understanding the index layout - Skip timeout specifications in FindElement() calls


Common Pitfalls & Solutions

Job Window Not Loading

// Problem: FindAllChildren() returns 0
// Solution: Use GetNewJobWindow() helper
var newJobWindow = GetNewJobWindow ();

// Helper waits until all children load
static AutomationElement GetNewJobWindow () {
    AutomationElement newJobWindow;
    do newJobWindow = Utils.MainWin.FindFirstChild ("NewJobWindow");
    while (newJobWindow.FindAllChildren ().Length = 0);
    return newJobWindow;
}

Parts Table Column Access

// Problem: Don't know which column index to use
// Solution: Use specific offsets:
// idx + 3  = Priority column
// idx + 6  = Group column

var idx = Array.FindIndex (partsTable.FindAllChildren (), a => a.Name = partName);
partsTable.FindChildAt (idx + 3).DoubleClick ();  // Priority
partsTable.FindChildAt (idx + 6).DoubleClick ();  // Group

Material Conversion Warning

// Problem: Dialog blocks further operations
// Solution: Check and click warning
var warning = scrollViewer.FindFirstChild (a => a.ByClassName ("Image"));
if (warning != null) {
    warning.Click ();
    Utils.MainWin.FindFirstChild ()
        .FindFirstChild (a => a.ByClassName ("ScrollViewer"))
        .FindFirstChild (a => a.ByName ("Ignore and convert"))?.Click ();
}

File Save Operations

// Problem: File not found immediately after save
// Solution: Use wait and sleep
Utils.WaitForFileExists (tempfile);
Thread.Sleep (500);  // Additional buffer
Assert.True (File.Exists (file));

Add Dialog Not Appearing

// Problem: Add dialog doesn't open
// Solution: Escape first, then open
Utils.Type (ESC);
Utils.Type ('a');

// Or use helper
OpenAddDialog ("NestingJobSample.fxjob");

Quick Reference: Operation Matrix

Operation Command Example

Open Job

Utils.OpenJobOrLayout (path)

Utils.OpenJobOrLayout (Utils.tData + "TwinJob.fxjob")

Create Job

CreateNewJob ()

var job = CreateNewJob ();

Get Job Window

GetNewJobWindow ()

var jobWin = GetNewJobWindow ();

Add Part

AddPartsOrLayouts (ELoadOption.Part, parts)

AddPartsOrLayouts (ELoadOption.Part, new[] { "123.fx" })

Set Priority

partsTable.FindChildAt (idx + 3).DoubleClick ()

Set via textbox entry

Set Group

partsTable.FindChildAt (idx + 6).DoubleClick ()

Set via textbox entry

Add Layout

job.FindFirstChild ("cAddLayout").Click ()

Loop for multiple

Renest

job.FindFirstChild ("cNesting").Click ()

Wait with WaitTillNestingCompletes()

Search Parts

GetSearchBox ().FindFirstChild (…​).AsTextBox ().Text

searchBox.Text = "PartName"

Sort Parts

GetComboBox ().Select ("Name")

Multiple sort options available

Validate Count

CheckPartsCountInLayout (expected)

CheckPartsCountInLayout (5)

Save Parts

job.FindFirstChild ("cSavePart").Click ()

Specify save folder

Export Sheet

g.FindFirstDescendant (a => a.ByName ("Export…​")).Click ()

DXF format

Import Sheet

job.FindFirstChild ("cImportSheet").Click ()

Choose DXF file

This recipe book covers all major patterns and commands used in the Job.cs framework for creating automated job management and nesting tests in the Flux application.

Contents

  • Table of Contents
  • Test Structure & Attributes
  • Basic Job Test Template
  • Test Attributes
  • IDisposable Pattern Setup
  • Constructor & Dispose Pattern
  • Machine Configuration
  • File Operations
  • Opening Job Files
  • File Access Paths
  • Cleanup Operations
  • Job Creation & Management
  • Creating New Job
  • Opening Jobs
  • Job Window Access
  • Job Dialog Operations
  • Layout Operations
  • Tab Navigation
  • Layout Management
  • Layout List Operations
  • Nesting Operations
  • Layout Switching
  • Layout Tree Dumping
  • Part Management
  • Parts Tab Operations
  • Adding Parts
  • Parts Table Access
  • Part Table Interactions
  • Part Operations
  • Part Quantity Management
  • Material Warning Handling
  • Part Validation
  • Sheet Management
  • Sheets Tab Operations
  • Sheets Table Access
  • Sheet Import/Export
  • Sheet Operations
  • Part Properties Configuration
  • Priority Settings
  • Rotation Settings
  • Grouping Settings
  • Dialog Interactions
  • New Job Dialog
  • Save Parts Dialog
  • Confirmation Dialogs
  • Save As Dialog
  • Add Dialog: Search & Sorting
  • Add Dialog Access
  • Search Operations
  • Sort Operations
  • Sort Direction
  • Add Dialog Parts List
  • Add Dialog UI Validation
  • Keyboard & Input Commands
  • Navigation Keys
  • Modifier Keys
  • Text Input
  • Mouse Operations
  • Basic Mouse Actions
  • Point Conversion
  • Drag & Drop
  • UI Element Access
  • Job Window Navigation
  • Tab Controls
  • Button Controls
  • Table Controls
  • ComboBox Controls
  • CheckBox Controls
  • TextBox Controls
  • Glob/Dialog Controls
  • Data Extraction & Validation
  • Extracting Job Data
  • Part Name Extraction
  • Sorting Validation
  • Layout Data Dumping
  • File Validation
  • Script Execution
  • Job Data Queries
  • Layout Validation
  • Layout Properties
  • Multi-Line Script
  • Common Job Workflows
  • Basic Job Creation Workflow
  • Job with Parts Addition Workflow
  • Layout Management Workflow
  • Priority Setting Workflow
  • Search & Sort Workflow
  • Copy/Paste Workflow
  • Helper Methods Reference
  • Job Operations
  • Layout Operations
  • Search & Sort
  • Data Extraction
  • Sorting & Comparison
  • Best Practices
  • Common Pitfalls & Solutions
  • Job Window Not Loading
  • Parts Table Column Access
  • Material Conversion Warning
  • File Save Operations
  • Add Dialog Not Appearing
  • Quick Reference: Operation Matrix
Job.cs Overview Software license

      Copyright © 2025 TRUMPF Metamation. All rights reserved.

    Compiled by Bento