/ src / modules / launcher / Wox.Test / ThemeHelperTest.cs
ThemeHelperTest.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;
  6  using System.Linq.Expressions;
  7  using ManagedCommon;
  8  using Microsoft.VisualStudio.TestTools.UnitTesting;
  9  using Moq;
 10  using PowerLauncher.Helper;
 11  using PowerLauncher.Services;
 12  
 13  namespace Wox.Test;
 14  
 15  [TestClass]
 16  public class ThemeHelperTest
 17  {
 18      // Registry key paths.
 19      private const string ThemesKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
 20      private const string PersonalizeKey = ThemesKey + "\\Personalize";
 21  
 22      // Theme paths.
 23      private const string HighContrastThemePath = @"C:\WINDOWS\resources\Ease of Access Themes\hcwhite.theme";
 24      private const string NonHighContrastThemePath = @"C:\Users\Test\AppData\Local\Microsoft\Windows\Themes\Custom.theme";
 25  
 26      /// <summary>
 27      /// The expected High Contrast theme when the <see cref="HighContrastThemePath"/> is returned
 28      /// from the registry.
 29      /// </summary>
 30      private const Theme HighContrastTheme = Theme.HighContrastWhite;
 31  
 32      /// <summary>
 33      /// Mock <see cref="IRegistryService.GetValue"/>, to return the value of the AppsUseLightTheme
 34      /// key.
 35      /// </summary>
 36      private static readonly Expression<Func<IRegistryService, object>> _mockAppsUseLightTheme = (service) =>
 37          service.GetValue(PersonalizeKey, "AppsUseLightTheme", ThemeHelper.AppsUseLightThemeDefault);
 38  
 39      /// <summary>
 40      /// Mock <see cref="IRegistryService.GetValue"/> to return the value of the CurrentTheme key.
 41      /// </summary>
 42      /// <remarks>
 43      /// The default value given here - string.Empty - must be the same as the default value in the
 44      /// actual code for tests using this mock to be valid.
 45      /// </remarks>
 46      private static readonly Expression<Func<IRegistryService, object>> _mockCurrentTheme = (service) =>
 47          service.GetValue(ThemesKey, "CurrentTheme", string.Empty);
 48  
 49      /// <summary>
 50      /// Test GetAppsTheme method.
 51      /// </summary>
 52      /// <param name="registryValue">The mocked value for the AppsUseLightTheme registry key.</param>
 53      /// <param name="expectedTheme">The expected <see cref="Theme"/> output from the call to
 54      /// <see cref="ThemeHelper.GetAppsTheme"/>.</param>
 55      [DataTestMethod]
 56      [DataRow(ThemeHelper.AppsUseLightThemeLight, Theme.Light)]
 57      [DataRow(ThemeHelper.AppsUseLightThemeDark, Theme.Dark)]
 58      [DataRow(int.MaxValue, Theme.Light)] // Out of range values should default to Light
 59      [DataRow(null, Theme.Light)] // Missing keys or values should default to Light
 60      [DataRow("RandomString", Theme.Light)] // Invalid string values should default to Light
 61      public void GetAppsTheme_ReturnsExpectedTheme(object registryValue, Theme expectedTheme)
 62      {
 63          var mockService = new Mock<IRegistryService>();
 64          mockService.Setup(_mockAppsUseLightTheme).Returns(registryValue);
 65  
 66          var helper = new ThemeHelper(mockService.Object);
 67  
 68          Assert.AreEqual(expectedTheme, helper.GetAppsTheme());
 69      }
 70  
 71      /// <summary>
 72      /// Test <see cref="ThemeHelper.GetHighContrastTheme"/>.
 73      /// </summary>
 74      /// <param name="registryValue">The mocked value for the CurrentTheme registry key.</param>
 75      /// <param name="expectedTheme">The expected <see cref="Theme"/> output from the call to
 76      /// <see cref="ThemeHelper.GetHighContrastTheme"/>.</param>
 77      [DataTestMethod]
 78      [DataRow(HighContrastThemePath, HighContrastTheme)] // Valid High Contrast theme
 79      [DataRow(NonHighContrastThemePath, null)] // Non-High Contrast theme should return null
 80      [DataRow(null, null)] // Missing keys or values should default to null
 81      [DataRow("", null)] // Empty string values should default to null
 82      public void GetHighContrastTheme_ReturnsExpectedTheme(string registryValue, Theme? expectedTheme)
 83      {
 84          var mockService = new Mock<IRegistryService>();
 85          mockService.Setup(_mockCurrentTheme).Returns(registryValue);
 86  
 87          var helper = new ThemeHelper(mockService.Object);
 88  
 89          Assert.AreEqual(expectedTheme, helper.GetHighContrastTheme());
 90      }
 91  
 92      /// <summary>
 93      /// Test <see cref="ThemeHelper.DetermineTheme"/>.
 94      /// </summary>
 95      /// <param name="registryTheme">The mocked value for the CurrentTheme registry key.</param>
 96      /// <param name="requestedTheme">The <see cref="Theme"/> value from the application's settings.
 97      /// </param>
 98      /// <param name="expectedTheme">The expected <see cref="Theme"/> output from the call to
 99      /// <see cref="ThemeHelper.DetermineTheme"/>.</param>
100      /// <param name="appsUseLightTheme">The mocked value for the AppsUseLightTheme registry key,
101      /// representing the system preference for Light or Dark mode.</param>
102      [DataTestMethod]
103      [DataRow(HighContrastThemePath, Theme.System, HighContrastTheme)] // High Contrast theme active
104      [DataRow(HighContrastThemePath, Theme.Light, HighContrastTheme)] // High Contrast theme active - Light mode override ignored
105      [DataRow(HighContrastThemePath, Theme.Dark, HighContrastTheme)] // High Contrast theme active - Dark mode override ignored
106      [DataRow(NonHighContrastThemePath, Theme.System, Theme.Light)] // System preference with default light theme
107      [DataRow(NonHighContrastThemePath, Theme.System, Theme.Dark, ThemeHelper.AppsUseLightThemeDark)] // System preference with dark mode
108      [DataRow(NonHighContrastThemePath, Theme.Light, Theme.Light, ThemeHelper.AppsUseLightThemeDark)] // Light mode override
109      [DataRow(NonHighContrastThemePath, Theme.Dark, Theme.Dark, ThemeHelper.AppsUseLightThemeLight)] // Dark mode override
110      [DataRow(null, Theme.System, Theme.Light)] // Missing keys or values should default to Light
111      [DataRow("", Theme.System, Theme.Light)] // Empty current theme paths should default to Light
112      [DataRow("RandomString", Theme.System, Theme.Light)] // Invalid current theme paths should default to Light
113      [DataRow(NonHighContrastThemePath, (Theme)int.MaxValue, Theme.Light)] // Invalid theme values should default to Light
114      public void DetermineTheme_ReturnsExpectedTheme(string registryTheme, Theme requestedTheme, Theme expectedTheme, int? appsUseLightTheme = 1)
115      {
116          var mockService = new Mock<IRegistryService>();
117          mockService.Setup(_mockCurrentTheme).Returns(registryTheme);
118          mockService.Setup(_mockAppsUseLightTheme).Returns(appsUseLightTheme);
119  
120          var helper = new ThemeHelper(mockService.Object);
121  
122          Assert.AreEqual(expectedTheme, helper.DetermineTheme(requestedTheme));
123      }
124  }