/ src / modules / Workspaces / WorkspacesEditorUITest / WorkspacesEditingPageTests.cs
WorkspacesEditingPageTests.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System.Diagnostics;
  6  using Microsoft.PowerToys.UITest;
  7  using Microsoft.VisualStudio.TestTools.UnitTesting;
  8  
  9  namespace WorkspacesEditorUITest;
 10  
 11  [TestClass]
 12  [Ignore("not stable")]
 13  public class WorkspacesEditingPageTests : WorkspacesUiAutomationBase
 14  {
 15      public WorkspacesEditingPageTests()
 16          : base()
 17      {
 18      }
 19  
 20      [TestMethod("WorkspacesEditingPage.RemoveApp")]
 21      [TestCategory("Workspaces Editing Page UI")]
 22      public void TestRemoveAppFromWorkspace()
 23      {
 24          // Find app list
 25          var appList = Find<Custom>("AppList");
 26          var initialAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
 27  
 28          if (initialAppCount == 0)
 29          {
 30              Assert.Inconclusive("No apps in workspace to remove");
 31              return;
 32          }
 33  
 34          // Remove first app
 35          var firstApp = appList.FindAll<Custom>(By.ClassName("AppItem"))[0];
 36          var appName = firstApp.GetAttribute("Name");
 37  
 38          var removeButton = firstApp.Find<Button>("Remove");
 39          removeButton.Click();
 40          Thread.Sleep(500);
 41  
 42          // Verify app removed from list
 43          var finalAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
 44          Assert.AreEqual(initialAppCount - 1, finalAppCount, "App should be removed from list");
 45  
 46          // Verify preview updated
 47          var previewPane = Find<Pane>("Preview");
 48          var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
 49          Assert.AreEqual(finalAppCount, windowPreviews.Count, "Preview should show correct number of windows");
 50      }
 51  
 52      [TestMethod("WorkspacesEditingPage.RemoveAndAddBackApp")]
 53      [TestCategory("Workspaces Editing Page UI")]
 54      public void TestRemoveAndAddBackApp()
 55      {
 56          // Find app list
 57          var appList = Find<Custom>("AppList");
 58          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
 59  
 60          if (apps.Count == 0)
 61          {
 62              Assert.Inconclusive("No apps in workspace to test");
 63              return;
 64          }
 65  
 66          var firstApp = apps[0];
 67          var appName = firstApp.GetAttribute("Name");
 68  
 69          // Remove app
 70          var removeButton = firstApp.Find<Button>("Remove");
 71          removeButton.Click();
 72          Thread.Sleep(500);
 73  
 74          // Verify removed app shows in "removed apps" section
 75          Assert.IsTrue(Has<Button>("Add back"), "Should have 'Add back' button for removed apps");
 76  
 77          // Add back the app
 78          var addBackButton = Find<Button>("Add back");
 79          addBackButton.Click();
 80          Thread.Sleep(500);
 81  
 82          // Verify app is back in the list
 83          var restoredApp = appList.Find<Custom>(By.Name(appName), timeoutMS: 2000);
 84          Assert.IsNotNull(restoredApp, "App should be restored to the list");
 85  
 86          // Verify preview updated
 87          var previewPane = Find<Pane>("Preview");
 88          var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
 89          var currentAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
 90          Assert.AreEqual(currentAppCount, windowPreviews.Count, "Preview should show all windows again");
 91      }
 92  
 93      [TestMethod("WorkspacesEditingPage.SetAppMinimized")]
 94      [TestCategory("Workspaces Editing Page UI")]
 95      public void TestSetAppMinimized()
 96      {
 97          // Find first app
 98          var appList = Find<Custom>("AppList");
 99          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
100  
101          if (apps.Count == 0)
102          {
103              Assert.Inconclusive("No apps in workspace to test");
104              return;
105          }
106  
107          var firstApp = apps[0];
108  
109          // Find and toggle minimized checkbox
110          var minimizedCheckbox = firstApp.Find<CheckBox>("Minimized");
111          bool wasMinimized = minimizedCheckbox.IsChecked;
112  
113          minimizedCheckbox.Click();
114          Thread.Sleep(500);
115  
116          // Verify state changed
117          Assert.AreNotEqual(wasMinimized, minimizedCheckbox.IsChecked, "Minimized state should toggle");
118  
119          // Verify preview reflects the change
120          var previewPane = Find<Pane>("Preview");
121          var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
122  
123          // The first window preview should indicate minimized state
124          if (minimizedCheckbox.IsChecked && windowPreviews.Count > 0)
125          {
126              var firstPreview = windowPreviews[0];
127              var opacity = firstPreview.GetAttribute("Opacity");
128  
129              // Minimized windows might have reduced opacity or other visual indicator
130              Assert.IsNotNull(opacity, "Minimized window should have visual indication in preview");
131          }
132      }
133  
134      [TestMethod("WorkspacesEditingPage.SetAppMaximized")]
135      [TestCategory("Workspaces Editing Page UI")]
136      public void TestSetAppMaximized()
137      {
138          // Find first app
139          var appList = Find<Custom>("AppList");
140          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
141  
142          if (apps.Count == 0)
143          {
144              Assert.Inconclusive("No apps in workspace to test");
145              return;
146          }
147  
148          var firstApp = apps[0];
149  
150          // Find and toggle maximized checkbox
151          var maximizedCheckbox = firstApp.Find<CheckBox>("Maximized");
152          bool wasMaximized = maximizedCheckbox.IsChecked;
153  
154          maximizedCheckbox.Click();
155          Thread.Sleep(500);
156  
157          // Verify state changed
158          Assert.AreNotEqual(wasMaximized, maximizedCheckbox.IsChecked, "Maximized state should toggle");
159  
160          // Verify preview reflects the change
161          var previewPane = Find<Pane>("Preview");
162          if (maximizedCheckbox.IsChecked)
163          {
164              // Maximized window should fill the preview area
165              var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
166              if (windowPreviews.Count > 0)
167              {
168                  var firstPreview = windowPreviews[0];
169  
170                  // Check if preview shows maximized state
171                  var width = firstPreview.GetAttribute("Width");
172                  var height = firstPreview.GetAttribute("Height");
173                  Assert.IsNotNull(width, "Maximized window should have width in preview");
174                  Assert.IsNotNull(height, "Maximized window should have height in preview");
175              }
176          }
177      }
178  
179      [TestMethod("WorkspacesEditingPage.LaunchAsAdmin")]
180      [TestCategory("Workspaces Editing Page UI")]
181      public void TestSetLaunchAsAdmin()
182      {
183          // Find app that supports admin launch
184          var appList = Find<Custom>("AppList");
185          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
186  
187          bool foundAdminCapableApp = false;
188          foreach (var app in apps)
189          {
190              try
191              {
192                  var adminCheckbox = app.Find<CheckBox>("Launch as admin", timeoutMS: 1000);
193                  if (adminCheckbox != null && adminCheckbox.IsChecked)
194                  {
195                      foundAdminCapableApp = true;
196                      bool wasAdmin = adminCheckbox.IsChecked;
197  
198                      adminCheckbox.Click();
199                      Thread.Sleep(500);
200  
201                      // Verify state changed
202                      Assert.AreNotEqual(wasAdmin, adminCheckbox.IsChecked, "Admin launch state should toggle");
203                      break;
204                  }
205              }
206              catch
207              {
208                  // This app doesn't support admin launch
209                  continue;
210              }
211          }
212  
213          if (!foundAdminCapableApp)
214          {
215              Assert.Inconclusive("No apps in workspace support admin launch");
216          }
217      }
218  
219      [TestMethod("WorkspacesEditingPage.AddCLIArgs")]
220      [TestCategory("Workspaces Editing Page UI")]
221      public void TestAddCommandLineArguments()
222      {
223          // Find first app
224          var appList = Find<Custom>("AppList");
225          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
226  
227          if (apps.Count == 0)
228          {
229              Assert.Inconclusive("No apps in workspace to test");
230              return;
231          }
232  
233          var firstApp = apps[0];
234  
235          // Find CLI args textbox
236          var cliArgsTextBox = firstApp.Find<TextBox>("Command line arguments", timeoutMS: 2000);
237          if (cliArgsTextBox == null)
238          {
239              Assert.Inconclusive("App does not support command line arguments");
240              return;
241          }
242  
243          // Add test arguments
244          string testArgs = "--test-arg value";
245          cliArgsTextBox.SetText(testArgs);
246          Thread.Sleep(500);
247  
248          // Verify arguments were entered
249          Assert.AreEqual(testArgs, cliArgsTextBox.Text, "Command line arguments should be set");
250      }
251  
252      [TestMethod("WorkspacesEditingPage.ChangeAppPosition")]
253      [TestCategory("Workspaces Editing Page UI")]
254      public void TestManuallyChangeAppPosition()
255      {
256          // Find first app
257          var appList = Find<Custom>("AppList");
258          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
259  
260          if (apps.Count == 0)
261          {
262              Assert.Inconclusive("No apps in workspace to test");
263              return;
264          }
265  
266          var firstApp = apps[0];
267  
268          // Find position controls
269          var xPositionBox = firstApp.Find<TextBox>("X position", timeoutMS: 2000);
270          var yPositionBox = firstApp.Find<TextBox>("Y position", timeoutMS: 2000);
271  
272          if (xPositionBox == null || yPositionBox == null)
273          {
274              // Try alternate approach with spinners
275              var positionSpinners = firstApp.FindAll<Custom>(By.ClassName("SpinBox"));
276              if (positionSpinners.Count >= 2)
277              {
278                  xPositionBox = positionSpinners[0].Find<TextBox>(By.ClassName("TextBox"));
279                  yPositionBox = positionSpinners[1].Find<TextBox>(By.ClassName("TextBox"));
280              }
281          }
282  
283          if (xPositionBox != null && yPositionBox != null)
284          {
285              // Change position
286              xPositionBox.SetText("200");
287              Thread.Sleep(500);
288  
289              yPositionBox.SetText("150");
290              Thread.Sleep(500);
291  
292              // Verify preview updated
293              var previewPane = Find<Pane>("Preview");
294              var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
295              Assert.IsTrue(windowPreviews.Count > 0, "Preview should show window at new position");
296          }
297          else
298          {
299              Assert.Inconclusive("Could not find position controls");
300          }
301      }
302  
303      [TestMethod("WorkspacesEditingPage.ChangeWorkspaceName")]
304      [TestCategory("Workspaces Editing Page UI")]
305      public void TestChangeWorkspaceName()
306      {
307          // Find workspace name textbox
308          var nameTextBox = Find<TextBox>("Workspace name");
309          string originalName = nameTextBox.Text;
310  
311          // Change name
312          string newName = "Renamed_Workspace_" + DateTime.Now.Ticks;
313          nameTextBox.SetText(newName);
314          Thread.Sleep(500);
315  
316          // Save changes
317          var saveButton = Find<Button>("Save");
318          saveButton.Click();
319          Thread.Sleep(1000);
320  
321          // Verify we're back at main list
322          Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list after saving");
323  
324          // Verify workspace was renamed
325          var workspacesList = Find<Custom>("WorkspacesList");
326          var renamedWorkspace = workspacesList.Find<Custom>(By.Name(newName), timeoutMS: 2000);
327          Assert.IsNotNull(renamedWorkspace, "Workspace should be renamed in the list");
328      }
329  
330      [TestMethod("WorkspacesEditingPage.SaveAndCancelWork")]
331      [TestCategory("Workspaces Editing Page UI")]
332      public void TestSaveAndCancelButtons()
333      {
334          // Make a change
335          var nameTextBox = Find<TextBox>("Workspace name");
336          string originalName = nameTextBox.Text;
337          string tempName = originalName + "_temp";
338  
339          nameTextBox.SetText(tempName);
340          Thread.Sleep(500);
341  
342          // Test Cancel button
343          var cancelButton = Find<Button>("Cancel");
344          cancelButton.Click();
345          Thread.Sleep(1000);
346  
347          // Verify returned to main list without saving
348          Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
349  
350          // Go back to editing
351          var workspacesList = Find<Custom>("WorkspacesList");
352          var workspace = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
353          workspace.Click();
354          Thread.Sleep(1000);
355  
356          // Verify name wasn't changed
357          nameTextBox = Find<TextBox>("Workspace name");
358          Assert.AreEqual(originalName, nameTextBox.Text, "Name should not be changed after cancel");
359  
360          // Now test Save button
361          nameTextBox.SetText(tempName);
362          Thread.Sleep(500);
363  
364          var saveButton = Find<Button>("Save");
365          saveButton.Click();
366          Thread.Sleep(1000);
367  
368          // Verify saved
369          workspacesList = Find<Custom>("WorkspacesList");
370          var savedWorkspace = workspacesList.Find<Custom>(By.Name(tempName), timeoutMS: 2000);
371          Assert.IsNotNull(savedWorkspace, "Workspace should be saved with new name");
372      }
373  
374      [TestMethod("WorkspacesEditingPage.NavigateWithoutSaving")]
375      [TestCategory("Workspaces Editing Page UI")]
376      public void TestNavigateToMainPageWithoutSaving()
377      {
378          // Make a change
379          var nameTextBox = Find<TextBox>("Workspace name");
380          string originalName = nameTextBox.Text;
381  
382          nameTextBox.SetText(originalName + "_unsaved");
383          Thread.Sleep(500);
384  
385          // Click on "Workspaces" navigation/breadcrumb
386          if (Has<NavigationViewItem>("Workspaces", timeoutMS: 1000))
387          {
388              var workspacesNav = Find<NavigationViewItem>("Workspaces");
389              workspacesNav.Click();
390              Thread.Sleep(1000);
391          }
392          else if (Has<HyperlinkButton>("Workspaces", timeoutMS: 1000))
393          {
394              var workspacesBreadcrumb = Find<HyperlinkButton>("Workspaces");
395              workspacesBreadcrumb.Click();
396              Thread.Sleep(1000);
397          }
398  
399          // If there's a confirmation dialog, handle it
400          if (Has<Button>("Discard", timeoutMS: 1000))
401          {
402              Find<Button>("Discard").Click();
403              Thread.Sleep(500);
404          }
405  
406          // Verify returned to main list
407          Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
408  
409          // Verify changes weren't saved
410          var workspacesList = Find<Custom>("WorkspacesList");
411          var unsavedWorkspace = workspacesList.Find<Custom>(By.Name(originalName + "_unsaved"), timeoutMS: 1000);
412          Assert.IsNull(unsavedWorkspace, "Unsaved changes should not persist");
413      }
414  
415      [TestMethod("WorkspacesEditingPage.CreateDesktopShortcut")]
416      [TestCategory("Workspaces Editing Page UI")]
417      public void TestCreateDesktopShortcut()
418      {
419          // Find desktop shortcut checkbox
420          var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
421  
422          // Get desktop path
423          string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
424  
425          // Get workspace name to check for shortcut
426          var nameTextBox = Find<TextBox>("Workspace name");
427          string workspaceName = nameTextBox.Text;
428          string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
429  
430          // Clean up any existing shortcut
431          if (File.Exists(shortcutPath))
432          {
433              File.Delete(shortcutPath);
434              Thread.Sleep(500);
435          }
436  
437          // Check the checkbox
438          if (!shortcutCheckbox.IsChecked)
439          {
440              shortcutCheckbox.Click();
441              Thread.Sleep(500);
442          }
443  
444          // Save
445          var saveButton = Find<Button>("Save");
446          saveButton.Click();
447          Thread.Sleep(2000); // Give time for shortcut creation
448  
449          // Verify shortcut was created
450          Assert.IsTrue(File.Exists(shortcutPath), "Desktop shortcut should be created");
451  
452          // Clean up
453          if (File.Exists(shortcutPath))
454          {
455              File.Delete(shortcutPath);
456          }
457      }
458  
459      [TestMethod("WorkspacesEditingPage.DesktopShortcutState")]
460      [TestCategory("Workspaces Editing Page UI")]
461      public void TestDesktopShortcutCheckboxState()
462      {
463          // Get workspace name
464          var nameTextBox = Find<TextBox>("Workspace name");
465          string workspaceName = nameTextBox.Text;
466          string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
467          string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
468  
469          // Find checkbox
470          var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
471  
472          // Test 1: When shortcut exists
473          if (!File.Exists(shortcutPath))
474          {
475              // Create shortcut first
476              if (!shortcutCheckbox.IsChecked)
477              {
478                  shortcutCheckbox.Click();
479                  Thread.Sleep(500);
480              }
481  
482              Find<Button>("Save").Click();
483              Thread.Sleep(2000);
484  
485              // Navigate back to editing
486              NavigateToEditingPage();
487          }
488  
489          shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
490          Assert.IsTrue(shortcutCheckbox.IsChecked, "Checkbox should be checked when shortcut exists");
491  
492          // Test 2: Remove shortcut
493          if (File.Exists(shortcutPath))
494          {
495              File.Delete(shortcutPath);
496              Thread.Sleep(500);
497          }
498  
499          // Re-navigate to refresh state
500          Find<Button>("Cancel").Click();
501          Thread.Sleep(1000);
502          NavigateToEditingPage();
503  
504          shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
505          Assert.IsFalse(shortcutCheckbox.IsChecked, "Checkbox should be unchecked when shortcut doesn't exist");
506      }
507  
508      [TestMethod("WorkspacesEditingPage.LaunchAndEdit")]
509      [TestCategory("Workspaces Editing Page UI")]
510      public void TestLaunchAndEditCapture()
511      {
512          // Find Launch and Edit button
513          var launchEditButton = Find<Button>("Launch and Edit");
514          launchEditButton.Click();
515          Thread.Sleep(3000); // Wait for apps to launch
516  
517          // Open a new application
518          Process.Start("calc.exe");
519          Thread.Sleep(2000);
520  
521          // Click Capture
522          var captureButton = Find<Button>("Capture");
523          captureButton.Click();
524          Thread.Sleep(2000);
525  
526          // Verify new app was added
527          var appList = Find<Custom>("AppList");
528          var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
529  
530          bool foundCalculator = false;
531          foreach (var app in apps)
532          {
533              var appName = app.GetAttribute("Name");
534              if (appName.Contains("Calculator", StringComparison.OrdinalIgnoreCase))
535              {
536                  foundCalculator = true;
537                  break;
538              }
539          }
540  
541          Assert.IsTrue(foundCalculator, "Newly opened Calculator should be captured and added");
542  
543          // Clean up
544          foreach (var process in Process.GetProcessesByName("CalculatorApp"))
545          {
546              process.Kill();
547          }
548  
549          foreach (var process in Process.GetProcessesByName("Calculator"))
550          {
551              process.Kill();
552          }
553      }
554  
555      // Helper methods
556      private void NavigateToEditingPage()
557      {
558          // Ensure we have at least one workspace
559          if (!Has<Custom>("WorkspacesList", timeoutMS: 1000))
560          {
561              CreateTestWorkspace();
562          }
563  
564          // Click on first workspace to edit
565          var workspacesList = Find<Custom>("WorkspacesList");
566          var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
567  
568          if (workspaceItems.Count == 0)
569          {
570              CreateTestWorkspace();
571              workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
572          }
573  
574          workspaceItems[0].Click();
575          Thread.Sleep(1000);
576      }
577  
578      private void CreateTestWorkspace()
579      {
580          // Open a test app
581          Process.Start("notepad.exe");
582          Thread.Sleep(1000);
583  
584          // Create workspace
585          var createButton = Find<Button>("Create Workspace");
586          createButton.Click();
587          Thread.Sleep(1000);
588  
589          // Capture
590          var captureButton = Find<Button>("Capture");
591          captureButton.Click();
592          Thread.Sleep(2000);
593  
594          // Save with default name
595          var saveButton = Find<Button>("Save");
596          saveButton.Click();
597          Thread.Sleep(1000);
598  
599          // Close test app
600          foreach (var process in Process.GetProcessesByName("notepad"))
601          {
602              process.Kill();
603          }
604      }
605  }