Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,36 +111,52 @@ internal static void ShowUpdateNotification(PSHostUserInterface hostUI)
? ManagedEntranceStrings.StableUpdateNotificationMessage
: ManagedEntranceStrings.PreviewUpdateNotificationMessage;

string notificationColor = string.Empty;
string resetColor = string.Empty;
string notificationMsg = FormatUpdateNotification(notificationMsgTemplate, releaseTag, hostUI.SupportsVirtualTerminal);

string line2Padding = string.Empty;
string line3Padding = string.Empty;
hostUI.WriteLine();
hostUI.WriteLine(notificationMsg);
}
}

// We calculate how much whitespace we need to make it look nice
if (hostUI.SupportsVirtualTerminal)
{
// Swaps foreground and background colors.
notificationColor = "\x1B[7m";
resetColor = "\x1B[0m";

// The first line is longest, if the message changes, this needs to be updated
int line1Length = notificationMsgTemplate.IndexOf('\n');
int line2Length = notificationMsgTemplate.IndexOf('\n', line1Length + 1);
int line3Length = notificationMsgTemplate.IndexOf('\n', line2Length + 1);
line3Length -= line2Length + 1;
line2Length -= line1Length + 1;

line2Padding = line2Padding.PadRight(line1Length - line2Length + releaseTag.Length);
// 3 represents the extra placeholder in the template
line3Padding = line3Padding.PadRight(line1Length - line3Length + 3);
}
/// <summary>
/// Formats the update notification message from the given template.
/// </summary>
/// <param name="notificationMsgTemplate">The three-line notification message template.</param>
/// <param name="releaseTag">The version of the new release.</param>
/// <param name="supportsVirtualTerminal">Whether to highlight the message and pad the shorter lines to the width of the first line.</param>
/// <returns>The formatted notification message.</returns>
internal static string FormatUpdateNotification(string notificationMsgTemplate, string releaseTag, bool supportsVirtualTerminal)
{
string notificationColor = string.Empty;
string resetColor = string.Empty;

string notificationMsg = string.Format(CultureInfo.CurrentCulture, notificationMsgTemplate, releaseTag, notificationColor, resetColor, line2Padding, line3Padding);
string line2Padding = string.Empty;
string line3Padding = string.Empty;

hostUI.WriteLine();
hostUI.WriteLine(notificationMsg);
// We calculate how much whitespace we need to make it look nice
if (supportsVirtualTerminal)
{
// Swaps foreground and background colors.
notificationColor = "\x1B[7m";
resetColor = "\x1B[0m";

// The first line is usually the longest, but localized templates may have a longer
// second or third line. Clamp the padding so it can't go negative and crash the host
// over a cosmetic alignment mismatch.
int line1Length = notificationMsgTemplate.IndexOf('\n');
int line2Length = notificationMsgTemplate.IndexOf('\n', line1Length + 1);
int line3Length = notificationMsgTemplate.IndexOf('\n', line2Length + 1);
line3Length -= line2Length + 1;
line2Length -= line1Length + 1;

line2Padding = line2Padding.PadRight(
Math.Max(0, line1Length - line2Length + releaseTag.Length));
// 3 represents the extra placeholder in the template
line3Padding = line3Padding.PadRight(
Math.Max(0, line1Length - line3Length + 3));
}

return string.Format(CultureInfo.CurrentCulture, notificationMsgTemplate, releaseTag, notificationColor, resetColor, line2Padding, line3Padding);
}

internal static async Task CheckForUpdates()
Expand Down
84 changes: 84 additions & 0 deletions test/xUnit/csharp/UpdatesNotificationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Globalization;

using Microsoft.PowerShell;
using Xunit;

namespace PSTests.Parallel
{
public static class UpdatesNotificationTests
{
private const string ReleaseTag = "7.6.7";

private static readonly string[] TemplateNames = new[]
{
nameof(ManagedEntranceStrings.StableUpdateNotificationMessage),
nameof(ManagedEntranceStrings.LTSUpdateNotificationMessage),
nameof(ManagedEntranceStrings.PreviewUpdateNotificationMessage),
};

// Keep in sync with the localized resources under src/Microsoft.PowerShell.ConsoleHost/resources.
private static readonly string[] LocalizedCultures = new[]
{
"cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-Hans", "zh-Hant",
};

public static IEnumerable<object[]> NeutralTemplates()
{
foreach (string templateName in TemplateNames)
{
yield return new object[] { templateName };
}
}

public static IEnumerable<object[]> LocalizedTemplates()
{
foreach (string cultureName in LocalizedCultures)
{
foreach (string templateName in TemplateNames)
{
yield return new object[] { cultureName, templateName };
}
}
}

[Theory]
[MemberData(nameof(NeutralTemplates))]
public static void TestNeutralTemplateLinesHaveSameWidth(string templateName)
{
string template = ManagedEntranceStrings.ResourceManager.GetString(templateName, CultureInfo.InvariantCulture);

string message = UpdatesNotification.FormatUpdateNotification(template, ReleaseTag, supportsVirtualTerminal: true);

string[] lines = message.Split('\n');
Assert.Equal(lines[0].Length, lines[1].Length);
Assert.Equal(lines[0].Length, lines[2].Length);
}

[Theory]
[InlineData(" {1} Short {0} {2}\n {1} A second line that is longer than the first one:{3}{2}\n {1} https://aka.ms/PowerShell-Release?tag=v{0} {4}{2}\n")]
[InlineData(" {1} New release: v{0} {2}\n {1} Upgrade now:{3}{2}\n {1} https://aka.ms/PowerShell-Release?tag=v{0} {4}{2}\n")]
public static void TestTemplateWithLongerLaterLine(string template)
{
string message = UpdatesNotification.FormatUpdateNotification(template, ReleaseTag, supportsVirtualTerminal: true);

Assert.Contains($"tag=v{ReleaseTag}", message);
}

[SkippableTheory]
[MemberData(nameof(LocalizedTemplates))]
public static void TestLocalizedTemplate(string cultureName, string templateName)
{
string template = ManagedEntranceStrings.ResourceManager.GetString(templateName, new CultureInfo(cultureName));
string neutralTemplate = ManagedEntranceStrings.ResourceManager.GetString(templateName, CultureInfo.InvariantCulture);
Skip.If(template == neutralTemplate, $"The '{cultureName}' resources are not available.");

string message = UpdatesNotification.FormatUpdateNotification(template, ReleaseTag, supportsVirtualTerminal: true);

Assert.Contains($"tag=v{ReleaseTag}", message);
}
}
}