Nesting.cs Recipe Book
Table of Contents
Test Structure & Attributes
Basic Nesting Test Template
[TestFixture (1501, "Nesting Test", Module = "FluxCut", Subsystem = "Nesting")]
class Nesting {
const string fxFile = "123.fx";
const string testMachineID = "2230001";
[FixtureInitialize]
public void Init () {
// Setup machines and resources
Utils.InstallCutMachines (testMachineID);
}
[FixtureCleanUp]
public void CleanUp () {
// Clean temporary files
foreach (var file in Directory.GetFiles (Utils.tempPath)) {
if (file.Contains (".txt") || file.Contains (".png")) File.Delete (file);
}
Utils.UninstallMachine (testMachineID);
}
[Test (ID, "Test Description", Type = TestType.Functional)]
public void Test_CaseID () {
// Test implementation
}
}
Machine IDs
const string testMachineID = "2230001"; // Primary test machine
Utils.InstallCutMachines (testMachineID); // Install machines
Utils.UninstallMachine (testMachineID); // Uninstall after tests
Fixture Setup & Teardown
Installation & Configuration
// Install cutting machine(s)
[FixtureInitialize]
public void Init () => Utils.InstallCutMachines ("2230001");
// Set default machine via script
Utils.RunScript ("Flux.CutMachine.Default = Flux.CutMachine.Get (2230001);");
// Set part thickness via script
Utils.RunScript ("Flux.PartEditor.Current.Part.Info.Thickness = 4;");
Resource Cleanup
[FixtureCleanUp]
public void CleanUp () {
// Delete temporary .txt files
foreach (var file in Directory.GetFiles (Utils.tempPath)) {
if (file.Contains (".txt")) File.Delete (file);
}
// Delete temporary .png files
foreach (var file in Directory.GetFiles (Utils.tempPath)) {
if (file.Contains (".png")) File.Delete (file);
}
// Combined cleanup
foreach (var file in Directory.GetFiles (Utils.tempPath)) {
if (file.Contains (".txt") || file.Contains (".png")) File.Delete (file);
}
// Uninstall machine
Utils.UninstallMachine ("2230001");
}
File Operations
Opening Files
// Open part file
Utils.OpenFile (Utils.tData + "123.fx");
Utils.OpenFile (Utils.tData + fxFile);
// Open drawing file
Utils.OpenFile (Utils.tData + "thinComplex.dxf");
// Open with path construction
Utils.OpenFile (Utils.tData + filename);
File References
// Temporary paths
Utils.tempPath // "c:/programdata/metamation/temp/"
Utils.tempPath + "test.png"
Utils.tempPath + "temp.txt"
// Test data paths
Utils.tData // Test data directory
Utils.tData + "123.fx" // Primary test file
Utils.tData + "thinComplex.dxf"
Utils.tData + "Test_C122169/test_MovePartByDrag.txt"
Navigation & View Management
View Switching
// Switch to CutCAM view (laser tooling view)
Utils.Type ('l');
// Switch to Nesting view
Utils.Type ('n');
// Switch to settings
Utils.Type ('t');
Utils.Type ('a'); // Layout settings
// Access Layout view via script
Utils.RunScript ("Flux.PartEditor.Current.Skin = Layout");
Utils.RunScript ("Flux.PartEditor.Current.Skin = CutCAM");
Workflow Panel
// Open workflow panel
Utils.MainWin.FindFirstDescendant (e => e.ByName ("workflow")).Click ();
// Access workflow elements
var workflowElems = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("WorkflowPanel")).FindAllChildren ();
// Find Quick-Nest button
var qnIcon = workflowElems.First (a => a.Name.StartsWith ("Quick-Nest"));
var pt = qnIcon.GetClickablePoint ();
// Check if Layout exists
workflowElems.FirstOrDefault (a => a.Name.StartsWith ("Layout")) != null
Settings Panel
// Open settings
var settings = Utils.MainWin.FindFirstChild ("SheafEditor");
// Access settings elements
layoutSettings.FindFirstDescendant (a => a.ByName ("Cut View")).Click ();
layoutSettings.FindFirstChild (a => a.ByClassName ("ScrollViewer"));
// Navigate sections
settings.FindFirstChild (a => a.ByControlType (ControlType.Pane));
Quick-Nest Operations
Creating Quick-Nest
// Basic quick nest (entire part)
DoNewQuickNest ();
// Single-part quick nest
DoNewQuickNest (true);
// Multi-part quick nest
DoNewQuickNest (false);
// Internal implementation
internal static void DoNewQuickNest (bool singlePart = false) {
Utils.Type ('l'); // Switch to CutCAM view
Utils.MainWin.FindFirstDescendant (e => e.ByName ("workflow")).Click ();
var workflowElems = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("WorkflowPanel"))
.FindAllChildren ();
var qnIcon = workflowElems.First (a => a.Name.StartsWith ("Quick-Nest"));
var pt = qnIcon.GetClickablePoint ();
// Delete existing layout
if (workflowElems.FirstOrDefault (a => a.Name.StartsWith ("Layout")) != null) {
Utils.MouseClick (pt, CONTROL);
Utils.Type (LEFT); Utils.Type (ENTER);
}
// Create new quick nest
Utils.MouseClick (pt, singlePart ? SHIFT : None);
Thread.Sleep (500);
Utils.Type (ESCAPE);
}
Quick-Nest with Modifiers
// Single-part nest (with Shift key)
Utils.MouseClick (qnBtn, SHIFT);
// Standard multi-part nest
Utils.MouseClick (qnBtn);
// Delete existing nest
Utils.MouseClick (pt, CONTROL); // Ctrl+click
Utils.Type (LEFT); // Navigate to Yes
Utils.Type (ENTER); // Confirm
Part Selection
Selection Methods
// Select by point click
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
// Select by double-click
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)), doubleClick: true);
// Select by double-click (alternative syntax)
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90, 0)), doubleClick: true);
// Select similar parts
SelectPartsBy ("Similar");
// Select same parts
SelectPartsBy ("Same Parts");
// Select parts in row
SelectPartsBy ("Similar Parts in Row");
// Select parts in column
SelectPartsBy ("Similar Parts in Column");
Selection Helper
internal static AutomationElement SelectPartsBy (string option) {
var g = Utils.WaitForGlob ();
g.FindFirstChild (a => a.ByName (option)).Click ();
return g;
}
Glob Operations (Selection Dialog)
// Wait for selection dialog
var g = Utils.WaitForGlob ();
// Interact with glob options
g.FindFirstChild (a => a.ByName ("All Blocks")).Click ();
g.FindFirstChild (a => a.ByName ("Similar")).Click ();
g.FindFirstDescendant (a => a.ByName ("Explode")).Click ();
g.FindFirstChild (a => a.ByName ("Group")).Click ();
g.FindFirstDescendant (a => a.ByName ("Ungroup")).Click ();
// Return to access glob again
var g = Utils.WaitForGlob ();
Part Manipulation
Part Movement
// Convert measurement points to pixels
var p = Utils.MeasurePtsToPixel ([new (50, 90), new (150, 90)]);
// Move part by dragging
Utils.MouseClick (p[0]); // Select part
Utils.DragAndDrop (Mouse.Position, p[1]); // Drag from current to target
Utils.Type (ESC); // Finalize
// Move part by double-click + mouse move
Utils.MouseClick (p[0], true); // Double-click to enable move
Utils.MouseMove (p[1]); // Move to target position
Utils.Type (ESC); // Finalize
Creating Duplicates
// Duplicate with Ctrl+drag
var p = Utils.MeasurePtsToPixel ([new (50, 90), new (150, 90)]);
Utils.MouseClick (p[0]); // Enable edit mode
using (var ob = Keyboard.Pressing ((VirtualKeyShort)CONTROL))
Utils.DragAndDrop (Mouse.Position, p[1]); // Ctrl+drag
Utils.Type (ESC);
Creating Part Arrays
// Array along COLUMN direction
var p = Utils.MeasurePtsToPixel ([
new (50, 90), // Click point
new (87, 167), // Start point
new (190, 1500) // End point
]);
Utils.MouseClick (p[0]); // Click at corner
Utils.MoveGlob (); // Move any glob if needed
Utils.DragAndDrop (p[1], p[2]); // Drag to create array
// Array along ROW direction
Utils.Type (ESC);
Utils.Type ('a', CONTROL); // Ctrl+A to select all
Utils.MoveGlob ();
var p2 = Utils.MeasurePtsToPixel ([
new (90, 1360),
new (3000, 1500)
]);
Utils.DragAndDrop (p2[0], p2[1]);
Utils.Type (ESC);
Part Rotation
// Rotate with Ctrl+mouse wheel
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90, 0)), doubleClick: true);
using (Keyboard.Pressing ((VirtualKeyShort)CONTROL))
Mouse.Scroll (25); // Rotate by 25 units
Grouping Parts
// Group parts
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
SelectPartsBy ("Similar").FindFirstDescendant (a => a.ByName ("Group")).Click ();
Utils.Type (ESC);
// Ungroup parts
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
Utils.WaitForGlob ().FindFirstDescendant (a => a.ByName ("Ungroup")).Click ();
Creating Overlaps
// Move part to create overlap
var p = Utils.MeasurePtsToPixel ([new (50, 90), new (150, 90)]);
Utils.MouseClick (p[0]);
using var ob = Keyboard.Pressing ((VirtualKeyShort)CONTROL);
Utils.DragAndDrop (p[0], p[1]);
Keyboard & Input Commands
Navigation Keys
// View switching
Utils.Type ('l'); // CutCAM view
Utils.Type ('n'); // Nesting view
Utils.Type ('t'); // Settings
Utils.Type ('a'); // Layout/Cut settings
Utils.Type ('w'); // Window/Workspace
Utils.Type ('s'); // Select/Settings
Utils.Type ('k'); // Laser CAM settings
// Finalization
Utils.Type (ESC); // Escape/Cancel
Utils.Type (ESCAPE); // Alternative Escape
Utils.Type (ENTER); // Confirm
Utils.Type (LEFT); // Navigate left
Modifier Keys
using static FluxTester.Utils.KeyShort;
// Key constants
CONTROL // Ctrl key
SHIFT // Shift key
ESC // Escape key
ENTER // Enter key
TAB // Tab key
LEFT // Left arrow
RIGHT // Right arrow
Multi-Key Input
// Sequential key presses
Utils.Type ('t'); Utils.Type ('a'); // Settings > Layout
// Type character with modifier
Utils.Type ('a', CONTROL); // Ctrl+A (select all)
// Access key shortcuts with KeyShort
using (Keyboard.Pressing ((VirtualKeyShort)CONTROL))
Mouse.Scroll (25); // Ctrl+mouse wheel
using (Keyboard.Pressing ((VirtualKeyShort)SHIFT))
Utils.DragAndDrop (start, end); // Shift+drag
Mouse Operations
Basic Mouse Actions
// Single click
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
// Double click
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)), doubleClick: true);
// Click with modifier
Utils.MouseClick (pt, CONTROL); // Ctrl+click
Utils.MouseClick (pt, SHIFT); // Shift+click
// Get current mouse position
var currentPos = Mouse.Position;
Measurement & Conversion
// Single point conversion
var pixelPt = Utils.MeasurePtToPixel (new (X, Y, Z));
var pixelPt = Utils.MeasurePtToPixel (new (153.946, 91.549, 0));
// Multiple points conversion
var pixels = Utils.MeasurePtsToPixel ([
new (50, 90),
new (87, 167),
new (190, 1500)
]);
// Access individual points
pixels[0] // First point
pixels[1] // Second point
pixels[2] // Third point
Drag & Drop
// Simple drag and drop
Utils.DragAndDrop (startPoint, endPoint);
// Drag from current position
Utils.DragAndDrop (Mouse.Position, p[1]);
// Drag with modifier key (Ctrl+drag)
using (Keyboard.Pressing ((VirtualKeyShort)CONTROL))
Utils.DragAndDrop (start, end);
// Drag with Shift (twinline nesting)
using (var ob = Keyboard.Pressing ((VirtualKeyShort)SHIFT))
Utils.DragAndDrop (start, end);
Mouse Movement
// Move mouse to position
Utils.MouseMove (Utils.MeasurePtToPixel (new (150, 90)));
// Move glob if in the way
Utils.MoveGlob ();
Mouse Wheel
// Scroll/rotate with modifier
using (Keyboard.Pressing ((VirtualKeyShort)CONTROL))
Mouse.Scroll (25); // Rotate parts by 25 units
Renesting Operations
Renest Method
// Renest with specific option
Renest (RenestOption.RearrangeToCreateRemSheet);
Renest (RenestOption.RearrangeToFullSheet);
Renest (RenestOption.SpreadPartsAlongX);
Renest (RenestOption.SpreadPartsAlongY);
Renest (RenestOption.SpreadPartsAlongXY);
// Renest by enum cast (0-4)
for (int i = 0; i < 5; i++) {
Renest ((RenestOption)i);
}
// Internal implementation
internal static void Renest (RenestOption option) {
Utils.RunScript ("Flux.PartEditor.Current.Skin = Layout");
Utils.FindElement (() => Utils.MainWin.FindFirstChild ("CmdBar")
.FindFirstChild (a => a.ByName ("nesting"))).Click ();
char key = (option) switch {
RenestOption.RearrangeToCreateRemSheet => 'a',
RenestOption.RearrangeToFullSheet => 'b',
RenestOption.SpreadPartsAlongX => 'c',
RenestOption.SpreadPartsAlongY => 'd',
RenestOption.SpreadPartsAlongXY => 'e',
RenestOption.Cancel or _ => 'f',
};
Utils.FindElement (() => Utils.MainWin.FindFirstChild ()
.FindFirstChild (a => a.ByControlType (ControlType.MenuItem)));
Utils.Type (key);
}
RenestOption Enum
internal enum RenestOption {
RearrangeToCreateRemSheet = 0, // 'a'
RearrangeToFullSheet = 1, // 'b'
SpreadPartsAlongX = 2, // 'c'
SpreadPartsAlongY = 3, // 'd'
SpreadPartsAlongXY = 4, // 'e'
Cancel = 5 // 'f'
}
Recompute Operations
Recompute Method
// Default recompute (retool all elements)
Recompute ();
// Specific recompute option
Recompute (RecomputeOption.RetoolAllElements);
Recompute (RecomputeOption.ToolOnlyUntooledElements);
Recompute (RecomputeOption.Sequence);
Recompute (RecomputeOption.Routing);
// Internal implementation
internal static void Recompute (RecomputeOption option = RecomputeOption.RetoolAllElements) {
Utils.Type (ESC);
bool skinIsLayout = Utils.RunScriptWithOutput ("var result = Flux.PartEditor.Current.Skin.ToString()")
= "Layout";
Utils.FindElement (() => Utils.MainWin.FindFirstChild ("CmdBar")
.FindFirstChild (a => a.ByName ("recompute"))).Click ();
char key = (option) switch {
RecomputeOption.RetoolAllElements => skinIsLayout ? 'd' : 'a',
RecomputeOption.ToolOnlyUntooledElements => skinIsLayout ? 'a' : 'b',
RecomputeOption.Sequence => skinIsLayout ? 'b' : 'c',
RecomputeOption.Routing => skinIsLayout ? 'c' : 'd',
_ => skinIsLayout ? 'd' : 'e'
};
Utils.FindElement (() => Utils.MainWin.FindFirstChild ()
.FindFirstChild (a => a.ByControlType (ControlType.MenuItem)));
Utils.Type (key);
}
RecomputeOption Enum
internal enum RecomputeOption {
RetoolAllElements = 0, // CutCAM: 'a', Layout: 'd'
ToolOnlyUntooledElements = 1, // CutCAM: 'b', Layout: 'a'
Sequence = 2, // CutCAM: 'c', Layout: 'b'
Routing = 3, // CutCAM: 'd', Layout: 'c'
Cancel = 4 // CutCAM: 'e', Layout: 'd'
}
Validation & Assertions
Layout Data Validation
// Check part placement in layout
CheckPartsInLayout (folder: "C122169", filename: "test_MovePartByDrag");
CheckPartsInLayout ("C122170", "test_MovePartBySelect");
CheckPartsInLayout ("C122172", "test_CreatePartsArray");
// Internal implementation
internal static void CheckPartsInLayout (string folder, string filename) {
List<string> commands = [
"StringBuilder layData = new ();",
"Flux.Layout.Current.Dump (layData)",
"var result = layData.ToString ();"
];
string output = Utils.RunScriptWithOutput (commands);
Assert.AreTextEqual ($"{Utils.tData}Test_{folder}/{filename}.txt", output);
}
Selected Parts Validation
// Check selected part outlines
CheckSelectedParts ("C123187", "test_Similar Parts in Row");
CheckSelectedParts ("C123186", "test_grouped");
CheckSelectedParts ("C123186", "test_ungrouped");
// Internal implementation
internal static void CheckSelectedParts (string folder, string filename) {
List<string> commands = [
"StringBuilder selectedParts = new ();",
"foreach (var part in Flux.Layout.Current.Children) { " +
" if (part.IsSelected) { " +
" foreach (var v in part.PartOutlines) selectedParts.Append (v.ToString () + '\\n');" +
" }" +
"}",
"var result = selectedParts.ToString ();"
];
Assert.AreTextEqual ($"{Utils.tData}Test_{folder}/{filename}.txt", Utils.RunScriptWithOutput (commands));
}
Visual Validation (Screenshots)
// Render and compare screen
CheckLuxScreen ("C123184", "test_OverlappingParts");
CheckLuxScreen ("C200391", "supportPins");
// Internal implementation
internal static void CheckLuxScreen (string folder, string filename = "test") {
string path = Utils.tempPath + "test.png";
Utils.RenderSceneToFile (path);
Assert.AreBitmapsEqual ($"{Utils.tData}Test_{folder}/{filename}.png", path, 95);
}
Script-Based Assertions
// Script with output validation
List<string> commands = [
"int count = 0;",
"foreach (var v in Flux.Part.Current.CutTech.AllSteps) { if (v.IsSelected) count++; }",
"var result = count.ToString();"
];
Assert.AreEqual (Utils.RunScriptWithOutput (commands), "55");
// Twinline validation
List<string> commands = [
"StringBuilder sb = new ();",
"bool twinline = true;",
"foreach (var v in Flux.Layout.Current.Children) { if (v.Tech != Flux.CLGroup) twinline = false; }",
"var result = (twinline = true).ToString();"
];
Assert.AreEqual (Utils.RunScriptWithOutput (commands), "True");
Query-Based Assertions
// Check overlapping parts
bool hasOverlap = DoAnyPartOverlaps ();
Assert.False (hasOverlap);
// Check parts within sheet margin
bool withinMargin = AreAllPartsNestedWithinSheet (includeTooling: true);
Assert.True (withinMargin);
Script Execution
Simple Script Execution
// Run script without output
Utils.RunScript ("Flux.PartEditor.Current.Part.Info.Thickness = 4;");
// Set default machine
Utils.RunScript ("Flux.CutMachine.Default = Flux.CutMachine.Get (2230001);");
// Switch view
Utils.RunScript ("Flux.PartEditor.Current.Skin = Layout");
Utils.RunScript ("Flux.PartEditor.Current.Skin = CutCAM");
Script with Output
// Single line script
var result = Utils.RunScriptWithOutput ("var result = Flux.Part.Current.Name;");
// Get current skin
bool skinIsLayout = Utils.RunScriptWithOutput ("var result = Flux.PartEditor.Current.Skin.ToString()")
= "Layout";
// Multi-line script
var result = Utils.RunScriptWithOutput (new List<string> {
"int count = 0;",
"foreach (var v in Flux.Part.Current.CutTech.AllSteps) { if (v.IsSelected) count++; }",
"var result = count.ToString();"
});
Complex Multi-Line Scripts
// Layout data dump
List<string> commands = [
"StringBuilder layData = new ();",
"Flux.Layout.Current.Dump (layData)",
"var result = layData.ToString ();"
];
string output = Utils.RunScriptWithOutput (commands);
// Selected parts collection
List<string> commands = [
"StringBuilder selectedParts = new ();",
"foreach (var part in Flux.Layout.Current.Children) { " +
" if (part.IsSelected) { " +
" foreach (var v in part.PartOutlines) selectedParts.Append (v.ToString () + '\\n');" +
" }" +
"}",
"var result = selectedParts.ToString ();"
];
// Twinline check
List<string> commands = [
"StringBuilder sb = new ();",
"bool twinline = true;",
"foreach (var v in Flux.Layout.Current.Children) { if (v.Tech != Flux.CLGroup) twinline = false; }",
"var result = (twinline = true).ToString();"
];
// Sheet margin check
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 Nesting Workflows
Basic Part Movement Workflow
[Test (428, "Moving the part by dragging")]
public void Test_C122169 () {
// 1. Open file and create quick nest
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (true);
// 2. Get target coordinates
var p = Utils.MeasurePtsToPixel ([new (50, 90), new (150, 90)]);
// 3. Move part
Utils.MouseClick (p[0]);
Utils.DragAndDrop (Mouse.Position, p[1]);
Utils.Type (ESC);
// 4. Validate
CheckPartsInLayout (folder: "C122169", filename: "test_MovePartByDrag");
}
Part Selection Workflow
[Test (437, "Parts Selection")]
public void Test_C123187 () {
string[] options = ["Same Parts", "Similar Parts in Row", "Similar Parts in Column"];
for (int i = 0; i < 3; i++) {
string option = options[i];
// 1. Setup
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (false);
// 2. Select part
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
// 3. Apply selection mode
SelectPartsBy (option);
// 4. Validate
CheckSelectedParts ("C123187", $"test_{option}");
}
}
Grouping Workflow
[Test (1513, "Group parts together")]
public void Test_C123186 () {
// Setup
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest ();
// GROUPING PHASE
// 1. Select initial part
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
// 2. Select similar and group
SelectPartsBy ("Similar").FindFirstDescendant (a => a.ByName ("Group")).Click ();
Utils.Type (ESC);
// 3. Verify group selection
Utils.MouseClick (Utils.MeasurePtToPixel (new (150, 90)));
CheckSelectedParts ("C123186", "test_grouped");
// UNGROUPING PHASE
// 4. Select part and ungroup
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
Utils.WaitForGlob ().FindFirstDescendant (a => a.ByName ("Ungroup")).Click ();
// 5. Verify ungrouped
Utils.MouseClick (Utils.MeasurePtToPixel (new (150, 90)));
CheckSelectedParts ("C123186", "test_ungrouped");
}
Quick-Nest with Settings Workflow
[Test (1514, "Nest start corner option")]
public void Test_C123192 () {
int index = 0;
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (true);
foreach (var type in new string[] { "Bottom left", "Bottom right", "Top left", "Top right" }) {
// 1. Open workflow
Utils.MainWin.FindFirstDescendant (e => e.ByName ("workflow")).Click ();
// 2. Access settings
var qnBtn = Utils.FindElement (() => Utils.MainWin.FindFirstChild ("WorkflowPanel"))
.FindAllChildren ().First (a => a.Name.StartsWith ("Quick-Nest"))
.GetClickablePoint ();
Utils.MouseClick (qnBtn);
// 3. Select corner option
var qnSettings = Utils.MainWin.FindFirstChild ("SheafEditor");
var combo = qnSettings.FindFirstChild (a => a.ByControlType (ControlType.Pane))
.FindAllChildren (a => a.ByControlType (ControlType.ComboBox))[1]
.AsComboBox ();
combo.Expand ();
combo.Items[index++].Click ();
// 4. Create new nest
Utils.MouseClick (qnBtn);
DoNewQuickNest (true);
// 5. Validate
CheckPartsInLayout (folder: "C123192", filename: $"test_{type}");
}
}
Renesting Workflow
[Test (1517, "Nesting options")]
public void Test_C145736 () {
string[] nestConds = ["CreateRemSheet", "UseFullSheet", "SpreadAlongX", "SpreadAlongY", "SpreadAlongXandY"];
// Setup
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (singlePart: true);
// Create some parts
var p = Utils.MeasurePtsToPixel ([new (50, 90), new (87, 167), new (1500, 1500)]);
Utils.MouseClick (p[0]);
Utils.MoveGlob ();
Utils.DragAndDrop (p[1], p[2]);
Thread.Sleep (1000);
Utils.Type (ESC);
// Test each renest option
for (int i = 0; i < 5; i++) {
Renest ((RenestOption)i);
CheckPartsInLayout ("C145736", $"test_{nestConds[i]}");
Thread.Sleep (2000);
}
}
Twinline Nesting Workflow
[Test (1518, "Manual Twinline Nesting")]
public void Test_C150617 () {
// Setup
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (singlePart: true);
// Select and position
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
Utils.MoveGlob ();
// Create twinline with Shift+drag
using (var ob = Keyboard.Pressing ((VirtualKeyShort)SHIFT))
Utils.DragAndDrop (
Utils.MeasurePtToPixel (new (87, 167)),
Utils.MeasurePtToPixel (new (3000, 1500))
);
Thread.Sleep (500);
Utils.Type (ESC);
// Validate twinline
List<string> commands = [
"StringBuilder sb = new ();",
"bool twinline = true;",
"foreach (var v in Flux.Layout.Current.Children) { if (v.Tech != Flux.CLGroup) twinline = false; }",
"var result = (twinline = true).ToString();"
];
Assert.AreEqual (Utils.RunScriptWithOutput (commands), "True");
}
Array Creation Workflow
[Test (1511, "Creating array of parts in layout")]
public void Test_C122172 () {
Utils.OpenFile (Utils.tData + fxFile);
DoNewQuickNest (true);
var p = Utils.MeasurePtsToPixel ([
new (50, 90), // Click point
new (87, 167), // Column start
new (190, 1500), // Column end
new (90, 1360), // Row start
new (3000, 1500) // Row end
]);
// Column array
Utils.MouseClick (p[0]);
Utils.MoveGlob ();
Utils.DragAndDrop (p[1], p[2]);
Utils.Type (ESC);
// Row array
Utils.Type ('a', CONTROL); // Select all
Utils.MoveGlob ();
Utils.DragAndDrop (p[3], p[4]);
Utils.Type (ESC);
// Validate
CheckPartsInLayout ("C122172", "test_CreatePartsArray");
}
Helper Methods Reference
UI Navigation Helpers
// Find main window
Utils.MainWin
// Get clickable point
element.GetClickablePoint ()
// Find element with retry
Utils.FindElement (() => Utils.MainWin.FindFirstChild ("CmdBar"))
// Retry settings
var r = Utils.RetrySettings ();
// Get desktop
var desktop = new UIA3Automation ().GetDesktop ()
Layout & Part Queries
// Check for overlaps
DoAnyPartOverlaps ()
// Returns: bool.Parse (Utils.RunScriptWithOutput (...))
// Check margin fit
AreAllPartsNestedWithinSheet (includeTooling: false)
// Returns: bool based on Flux script
// Count selected
int selectedCount = ... // Via script iteration
// Get part count
Flux.Layout.Current.Children.Count // Via script
Timing & Sync
Thread.Sleep (500); // 500ms delay
Thread.Sleep (1000); // 1 second
Thread.Sleep (2000); // 2 seconds
Utils.WaitForGlob (); // Wait for dialog
Utils.FindElement (...) // Wait with retry
File Operations
// Cleanup
foreach (var file in Directory.GetFiles (Utils.tempPath)) {
if (file.Contains (".txt") || file.Contains (".png"))
File.Delete (file);
}
// Write files (via script)
Utils.RenderSceneToFile (path);
Best Practices
✅ DO: - Use DoNewQuickNest() for consistent nest creation - Always
call Utils.Type (ESC) to finalize operations - Use measurement point
conversion for consistent coordinates - Validate with both layout data
AND visual comparison - Clean up temporary files in
[FixtureCleanUp] - Use meaningful variable names for part
coordinates - Add comments for non-obvious UI operations - Use helper
methods for repeated patterns
❌ DON’T: - Hardcode pixel coordinates (use measurement points) - Skip
Utils.MoveGlob() when dragging near dialogs - Forget Thread.Sleep()
for async operations - Mix view switches without proper navigation -
Create multiple quick-nests without cleanup - Skip assertions in
validation - Use generic names (p, g, r, etc. without context) - Forget
to close settings/dialogs with ESC
Common Pitfalls & Solutions
Glob Interference
// Problem: Glob blocks drag operation
// Solution: Move glob before dragging
Utils.MoveGlob ();
Utils.DragAndDrop (start, end);
View State Confusion
// Problem: Don't know which view is active
// Solution: Check via script
bool skinIsLayout = Utils.RunScriptWithOutput ("var result = Flux.PartEditor.Current.Skin.ToString()") = "Layout";
Timing Issues
// Problem: Operation not complete
// Solution: Add appropriate delays
Utils.MouseClick (pt);
Thread.Sleep (500);
Utils.Type (ESC);
Selection Not Persisting
// Problem: Selection lost after operation
// Solution: Re-select before validation
Utils.MouseClick (Utils.MeasurePtToPixel (new (50, 90)));
SelectPartsBy ("Similar");
CheckSelectedParts (...);
Quick Reference Cheat Sheet
| Operation | Command |
|---|---|
Create quick nest |
|
Select parts |
|
Move part |
|
Rotate part |
|
Create duplicate |
|
Create twinline |
|
Group parts |
|
Renest |
|
Recompute |
|
Validate layout |
|
Validate selection |
|
Visual validation |
|
Switch view |
|
This recipe book covers all major patterns and commands used in the Nesting.cs framework for creating automated layout and nesting tests in the Flux application.