fb597ed304
## Summary of the Pull Request This PR adds support for renaming windows. ![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif) ![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif) It does so through two new actions: * `renameWindow` takes a `name` parameter, and attempts to set the window's name to the provided name. This is useful if you always want to hit <kbd>F3</kbd> and rename a window to "foo" (READ: probably not that useful) * `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a `TextBox`. When the user hits Ok, it'll request a rename for the provided value. This lets the user pick a new name for the window at runtime. In both cases, if there's already a window with that name, then the monarch will reject the rename, and pop a `Toast` in the window informing the user that the rename failed. Nifty! ## References * Builds on the toasts from #9523 * #5000 - process model megathread ## PR Checklist * [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747 * [x] I work here * [x] Tests addded (and pass with the help of #9660) * [ ] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews. > PAIN: We can't immediately focus the textbox in the TeachingTip. It's > not technically focusable until it is opened. However, it doesn't > provide an even tto tell us when it is opened. That's tracked in > microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to > click on the text box manually. > We're also not using a ContentDialog for this, because in Xaml > Islands a text box in a ContentDialog won't recieve _any_ keypresses. > Fun! ## Validation Steps Performed I've been playing with ```json { "keys": "f1", "command": "identifyWindow" }, { "keys": "f2", "command": "identifyWindows" }, { "keys": "f3", "command": "openWindowRenamer" }, { "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } }, { "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } }, ``` and they seem to work as expected
1008 lines
41 KiB
C++
1008 lines
41 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
|
|
#include "../TerminalApp/TerminalPage.h"
|
|
#include "../TerminalApp/MinMaxCloseControl.h"
|
|
#include "../TerminalApp/TabRowControl.h"
|
|
#include "../TerminalApp/ShortcutActionDispatch.h"
|
|
#include "../TerminalApp/TerminalTab.h"
|
|
#include "../TerminalApp/CommandPalette.h"
|
|
#include "../CppWinrtTailored.h"
|
|
|
|
using namespace Microsoft::Console;
|
|
using namespace TerminalApp;
|
|
using namespace winrt::TerminalApp;
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|
|
|
using namespace WEX::Logging;
|
|
using namespace WEX::TestExecution;
|
|
using namespace WEX::Common;
|
|
|
|
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
|
using namespace winrt::Windows::Foundation::Collections;
|
|
using namespace winrt::Windows::System;
|
|
using namespace winrt::Windows::UI::Xaml;
|
|
using namespace winrt::Windows::UI::Xaml::Controls;
|
|
using namespace winrt::Windows::UI::Core;
|
|
using namespace winrt::Windows::UI::Text;
|
|
|
|
namespace winrt
|
|
{
|
|
namespace MUX = Microsoft::UI::Xaml;
|
|
namespace WUX = Windows::UI::Xaml;
|
|
using IInspectable = Windows::Foundation::IInspectable;
|
|
}
|
|
|
|
namespace TerminalAppLocalTests
|
|
{
|
|
// TODO:microsoft/terminal#3838:
|
|
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
|
// an updated TAEF that will let us install framework packages when the test
|
|
// package is deployed. Until then, these tests won't deploy in CI.
|
|
|
|
class TabTests
|
|
{
|
|
// For this set of tests, we need to activate some XAML content. For
|
|
// release builds, the application runs as a centennial application,
|
|
// which lets us run full trust, and means that we need to use XAML
|
|
// Islands to host our UI. However, in these tests, we don't really need
|
|
// to run full trust - we just need to get some UI elements created. So
|
|
// we can just rely on the normal UWP activation to create us.
|
|
//
|
|
// IMPORTANTLY! When tests need to make XAML objects, or do XAML things,
|
|
// make sure to use RunOnUIThread. This helper will dispatch a lambda to
|
|
// be run on the UI thread.
|
|
|
|
BEGIN_TEST_CLASS(TabTests)
|
|
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
|
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
|
END_TEST_CLASS()
|
|
|
|
// These four tests act as canary tests. If one of them fails, then they
|
|
// can help you identify if something much lower in the stack has
|
|
// failed.
|
|
TEST_METHOD(EnsureTestsActivate);
|
|
TEST_METHOD(TryCreateSettingsType);
|
|
TEST_METHOD(TryCreateConnectionType);
|
|
TEST_METHOD(TryCreateXamlObjects);
|
|
|
|
TEST_METHOD(TryInitializePage);
|
|
|
|
TEST_METHOD(CreateSimpleTerminalXamlType);
|
|
TEST_METHOD(CreateTerminalMuxXamlType);
|
|
|
|
TEST_METHOD(CreateTerminalPage);
|
|
|
|
TEST_METHOD(TryDuplicateBadTab);
|
|
TEST_METHOD(TryDuplicateBadPane);
|
|
|
|
TEST_METHOD(TryZoomPane);
|
|
TEST_METHOD(MoveFocusFromZoomedPane);
|
|
TEST_METHOD(CloseZoomedPane);
|
|
|
|
TEST_METHOD(NextMRUTab);
|
|
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
|
|
|
TEST_METHOD(TestWindowRenameSuccessful);
|
|
TEST_METHOD(TestWindowRenameFailure);
|
|
|
|
TEST_CLASS_SETUP(ClassSetup)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
TEST_METHOD_CLEANUP(MethodCleanup)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void _initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
|
|
CascadiaSettings initialSettings);
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> _commonSetup();
|
|
};
|
|
|
|
template<typename TFunction>
|
|
void TestOnUIThread(const TFunction& function)
|
|
{
|
|
const auto result = RunOnUIThread(function);
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::EnsureTestsActivate()
|
|
{
|
|
// This test was originally used to ensure that XAML Islands was
|
|
// initialized correctly. Now, it's used to ensure that the tests
|
|
// actually deployed and activated. This test _should_ always pass.
|
|
VERIFY_IS_TRUE(true);
|
|
}
|
|
|
|
void TabTests::TryCreateSettingsType()
|
|
{
|
|
// Verify we can create a WinRT type we authored
|
|
// Just creating it is enough to know that everything is working.
|
|
TerminalSettings settings;
|
|
VERIFY_IS_NOT_NULL(settings);
|
|
auto oldFontSize = settings.FontSize();
|
|
settings.FontSize(oldFontSize + 5);
|
|
auto newFontSize = settings.FontSize();
|
|
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
|
|
}
|
|
|
|
void TabTests::TryCreateConnectionType()
|
|
{
|
|
// Verify we can create a WinRT type we authored
|
|
// Just creating it is enough to know that everything is working.
|
|
winrt::Microsoft::Terminal::TerminalConnection::EchoConnection conn{};
|
|
VERIFY_IS_NOT_NULL(conn);
|
|
// We're doing this test separately from the TryCreateSettingsType test,
|
|
// to ensure both dependent binaries (TerminalSettings and
|
|
// TerminalConnection) both work individually.
|
|
}
|
|
|
|
void TabTests::TryCreateXamlObjects()
|
|
{
|
|
auto result = RunOnUIThread([]() {
|
|
VERIFY_IS_TRUE(true, L"Congrats! We're running on the UI thread!");
|
|
|
|
auto v = winrt::Windows::ApplicationModel::Core::CoreApplication::GetCurrentView();
|
|
VERIFY_IS_NOT_NULL(v, L"Ensure we have a current view");
|
|
// Verify we can create a some XAML objects
|
|
// Just creating all of them is enough to know that everything is working.
|
|
winrt::Windows::UI::Xaml::Controls::UserControl controlRoot;
|
|
VERIFY_IS_NOT_NULL(controlRoot, L"Try making a UserControl");
|
|
winrt::Windows::UI::Xaml::Controls::Grid root;
|
|
VERIFY_IS_NOT_NULL(root, L"Try making a Grid");
|
|
winrt::Windows::UI::Xaml::Controls::SwapChainPanel swapChainPanel;
|
|
VERIFY_IS_NOT_NULL(swapChainPanel, L"Try making a SwapChainPanel");
|
|
winrt::Windows::UI::Xaml::Controls::Primitives::ScrollBar scrollBar;
|
|
VERIFY_IS_NOT_NULL(scrollBar, L"Try making a ScrollBar");
|
|
});
|
|
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::CreateSimpleTerminalXamlType()
|
|
{
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::MinMaxCloseControl> mmcc{ nullptr };
|
|
|
|
auto result = RunOnUIThread([&mmcc]() {
|
|
mmcc = winrt::make_self<winrt::TerminalApp::implementation::MinMaxCloseControl>();
|
|
VERIFY_IS_NOT_NULL(mmcc);
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::CreateTerminalMuxXamlType()
|
|
{
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TabRowControl> tabRowControl{ nullptr };
|
|
|
|
auto result = RunOnUIThread([&tabRowControl]() {
|
|
tabRowControl = winrt::make_self<winrt::TerminalApp::implementation::TabRowControl>();
|
|
VERIFY_IS_NOT_NULL(tabRowControl);
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::CreateTerminalPage()
|
|
{
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
|
|
|
auto result = RunOnUIThread([&page]() {
|
|
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>();
|
|
VERIFY_IS_NOT_NULL(page);
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
// Method Description:
|
|
// - This is a helper to set up a TerminalPage for a unittest. This method
|
|
// does a couple things:
|
|
// * Create()'s a TerminalPage with the given settings. Constructing a
|
|
// TerminalPage so that we can get at its implementation is wacky, so
|
|
// this helper will do it correctly for you, even if this doesn't make a
|
|
// ton of sense on the surface. This is also why you need to pass both a
|
|
// projection and a com_ptr to this method.
|
|
// * It will use the provided settings object to initialize the TerminalPage
|
|
// * It will add the TerminalPage to the test Application, so that we can
|
|
// get actual layout events. Much of the Terminal assumes there's a
|
|
// non-zero ActualSize to the Terminal window, and adding the Page to
|
|
// the Application will make it behave as expected.
|
|
// * It will wait for the TerminalPage to finish initialization before
|
|
// returning control to the caller. It does this by creating an event and
|
|
// only setting the event when the TerminalPage raises its Initialized
|
|
// event, to signal that startup is complete. At this point, there will
|
|
// be one tab with the default profile in the page.
|
|
// * It will also ensure that the first tab is focused, since that happens
|
|
// asynchronously in the application typically.
|
|
// Arguments:
|
|
// - page: a TerminalPage implementation ptr that will receive the new TerminalPage instance
|
|
// - initialSettings: a CascadiaSettings to initialize the TerminalPage with.
|
|
// Return Value:
|
|
// - <none>
|
|
void TabTests::_initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
|
|
CascadiaSettings initialSettings)
|
|
{
|
|
// This is super wacky, but we can't just initialize the
|
|
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
|
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
|
// during TerminalPage::Create() below.
|
|
//
|
|
// Instead, create the winrt object, then get a com_ptr to the
|
|
// implementation _from_ the winrt object. This seems to work, even if
|
|
// it's weird.
|
|
winrt::TerminalApp::TerminalPage projectedPage{ nullptr };
|
|
|
|
Log::Comment(NoThrowString().Format(L"Construct the TerminalPage"));
|
|
auto result = RunOnUIThread([&projectedPage, &page, initialSettings]() {
|
|
projectedPage = winrt::TerminalApp::TerminalPage();
|
|
page.copy_from(winrt::get_self<winrt::TerminalApp::implementation::TerminalPage>(projectedPage));
|
|
page->_settings = initialSettings;
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
VERIFY_IS_NOT_NULL(page);
|
|
VERIFY_IS_NOT_NULL(page->_settings);
|
|
|
|
::details::Event waitForInitEvent;
|
|
if (!waitForInitEvent.IsValid())
|
|
{
|
|
VERIFY_SUCCEEDED(HRESULT_FROM_WIN32(::GetLastError()));
|
|
}
|
|
page->Initialized([&waitForInitEvent](auto&&, auto&&) {
|
|
waitForInitEvent.Set();
|
|
});
|
|
|
|
Log::Comment(L"Create() the TerminalPage");
|
|
|
|
result = RunOnUIThread([&page]() {
|
|
VERIFY_IS_NOT_NULL(page);
|
|
VERIFY_IS_NOT_NULL(page->_settings);
|
|
page->Create();
|
|
Log::Comment(L"Create()'d the page successfully");
|
|
|
|
// Build a NewTab action, to make sure we start with one. The real
|
|
// Terminal will always get one from AppCommandlineArgs.
|
|
NewTerminalArgs newTerminalArgs{};
|
|
NewTabArgs args{ newTerminalArgs };
|
|
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
|
|
// push the arg onto the front
|
|
page->_startupActions.Append(newTabAction);
|
|
Log::Comment(L"Added a single newTab action");
|
|
|
|
auto app = ::winrt::Windows::UI::Xaml::Application::Current();
|
|
|
|
winrt::TerminalApp::TerminalPage pp = *page;
|
|
winrt::Windows::UI::Xaml::Window::Current().Content(pp);
|
|
winrt::Windows::UI::Xaml::Window::Current().Activate();
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Wait for the page to finish initializing...");
|
|
VERIFY_SUCCEEDED(waitForInitEvent.Wait());
|
|
Log::Comment(L"...Done");
|
|
|
|
result = RunOnUIThread([&page]() {
|
|
// In the real app, this isn't a problem, but doesn't happen
|
|
// reliably in the unit tests.
|
|
Log::Comment(L"Ensure we set the first tab as the selected one.");
|
|
auto tab = page->_tabs.GetAt(0);
|
|
auto tabImpl = page->_GetTerminalTabImpl(tab);
|
|
page->_tabView.SelectedItem(tabImpl->TabViewItem());
|
|
page->_UpdatedSelectedTab(0);
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::TryInitializePage()
|
|
{
|
|
// This is a very simple test to prove we can create settings and a
|
|
// TerminalPage and not only create them successfully, but also create a
|
|
// tab using those settings successfully.
|
|
|
|
const std::string settingsJson0{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"profiles": [
|
|
{
|
|
"name" : "profile0",
|
|
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 1
|
|
},
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 2
|
|
}
|
|
]
|
|
})" };
|
|
|
|
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
|
VERIFY_IS_NOT_NULL(settings0);
|
|
|
|
// This is super wacky, but we can't just initialize the
|
|
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
|
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
|
// during TerminalPage::Create() below.
|
|
//
|
|
// Instead, create the winrt object, then get a com_ptr to the
|
|
// implementation _from_ the winrt object. This seems to work, even if
|
|
// it's weird.
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
|
_initializeTerminalPage(page, settings0);
|
|
|
|
auto result = RunOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::TryDuplicateBadTab()
|
|
{
|
|
// * Create a tab with a profile with GUID 1
|
|
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
|
// * Try calling _DuplicateFocusedTab on tab 1
|
|
// * No new tab should be created (and more importantly, the app should not crash)
|
|
//
|
|
// Created to test GH#2455
|
|
|
|
const std::string settingsJson0{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"profiles": [
|
|
{
|
|
"name" : "profile0",
|
|
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 1
|
|
},
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 2
|
|
}
|
|
]
|
|
})" };
|
|
|
|
const std::string settingsJson1{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"profiles": [
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 2
|
|
}
|
|
]
|
|
})" };
|
|
|
|
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
|
VERIFY_IS_NOT_NULL(settings0);
|
|
|
|
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
|
VERIFY_IS_NOT_NULL(settings1);
|
|
|
|
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
|
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
|
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
|
|
|
// This is super wacky, but we can't just initialize the
|
|
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
|
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
|
// during TerminalPage::Create() below.
|
|
//
|
|
// Instead, create the winrt object, then get a com_ptr to the
|
|
// implementation _from_ the winrt object. This seems to work, even if
|
|
// it's weird.
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
|
_initializeTerminalPage(page, settings0);
|
|
|
|
auto result = RunOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Duplicate the first tab");
|
|
result = RunOnUIThread([&page]() {
|
|
page->_DuplicateFocusedTab();
|
|
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(NoThrowString().Format(
|
|
L"Change the settings of the TerminalPage so the first profile is "
|
|
L"no longer in the list of profiles"));
|
|
result = RunOnUIThread([&page, settings1]() {
|
|
page->_settings = settings1;
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Duplicate the tab, and don't crash");
|
|
result = RunOnUIThread([&page]() {
|
|
page->_DuplicateFocusedTab();
|
|
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::TryDuplicateBadPane()
|
|
{
|
|
// * Create a tab with a profile with GUID 1
|
|
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
|
// * Try calling _SplitPane(Duplicate) on tab 1
|
|
// * No new pane should be created (and more importantly, the app should not crash)
|
|
//
|
|
// Created to test GH#2455
|
|
|
|
const std::string settingsJson0{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"profiles": [
|
|
{
|
|
"name" : "profile0",
|
|
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 1
|
|
},
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 2
|
|
}
|
|
]
|
|
})" };
|
|
|
|
const std::string settingsJson1{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"profiles": [
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"historySize": 2
|
|
}
|
|
]
|
|
})" };
|
|
|
|
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
|
VERIFY_IS_NOT_NULL(settings0);
|
|
|
|
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
|
VERIFY_IS_NOT_NULL(settings1);
|
|
|
|
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
|
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
|
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
|
|
|
// This is super wacky, but we can't just initialize the
|
|
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
|
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
|
// during TerminalPage::Create() below.
|
|
//
|
|
// Instead, create the winrt object, then get a com_ptr to the
|
|
// implementation _from_ the winrt object. This seems to work, even if
|
|
// it's weird.
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
|
_initializeTerminalPage(page, settings0);
|
|
|
|
auto result = RunOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
result = RunOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
|
|
result = RunOnUIThread([&page]() {
|
|
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
|
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(NoThrowString().Format(
|
|
L"Change the settings of the TerminalPage so the first profile is "
|
|
L"no longer in the list of profiles"));
|
|
result = RunOnUIThread([&page, settings1]() {
|
|
page->_settings = settings1;
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
|
|
result = RunOnUIThread([&page]() {
|
|
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
|
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2,
|
|
tab->GetLeafPaneCount(),
|
|
L"We should gracefully do nothing here - the profile no longer exists.");
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
auto cleanup = wil::scope_exit([] {
|
|
auto result = RunOnUIThread([]() {
|
|
// There's something causing us to crash north of
|
|
// TSFInputControl::NotifyEnter, or LayoutRequested. It's very
|
|
// unclear what that issue is. Since these tests don't run in
|
|
// CI, simply log a message so that the dev running these tests
|
|
// knows it's expected.
|
|
Log::Comment(L"This test often crashes on cleanup, even when it succeeds. If it succeeded, then crashes, that's okay.");
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - This is a helper method for setting up a TerminalPage with some common
|
|
// settings, and creating the first tab.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - The initialized TerminalPage, ready to use.
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> TabTests::_commonSetup()
|
|
{
|
|
const std::string settingsJson0{ R"(
|
|
{
|
|
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"showTabsInTitlebar": false,
|
|
"profiles": [
|
|
{
|
|
"name" : "profile0",
|
|
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
|
"tabTitle" : "Profile 0",
|
|
"historySize": 1
|
|
},
|
|
{
|
|
"name" : "profile1",
|
|
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
|
"tabTitle" : "Profile 1",
|
|
"historySize": 2
|
|
},
|
|
{
|
|
"name" : "profile2",
|
|
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
|
"tabTitle" : "Profile 2",
|
|
"historySize": 3
|
|
},
|
|
{
|
|
"name" : "profile3",
|
|
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
|
|
"tabTitle" : "Profile 3",
|
|
"historySize": 4
|
|
}
|
|
]
|
|
})" };
|
|
|
|
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
|
VERIFY_IS_NOT_NULL(settings0);
|
|
|
|
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
|
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
|
|
|
// This is super wacky, but we can't just initialize the
|
|
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
|
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
|
// during TerminalPage::Create() below.
|
|
//
|
|
// Instead, create the winrt object, then get a com_ptr to the
|
|
// implementation _from_ the winrt object. This seems to work, even if
|
|
// it's weird.
|
|
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
|
_initializeTerminalPage(page, settings0);
|
|
|
|
auto result = RunOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
return page;
|
|
}
|
|
|
|
void TabTests::TryZoomPane()
|
|
{
|
|
auto page = _commonSetup();
|
|
|
|
Log::Comment(L"Create a second pane");
|
|
auto result = RunOnUIThread([&page]() {
|
|
SplitPaneArgs args{ SplitType::Duplicate };
|
|
ActionEventArgs eventArgs{ args };
|
|
page->_HandleSplitPane(nullptr, eventArgs);
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Zoom in on the pane");
|
|
result = RunOnUIThread([&page]() {
|
|
ActionEventArgs eventArgs{};
|
|
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Zoom out of the pane");
|
|
result = RunOnUIThread([&page]() {
|
|
ActionEventArgs eventArgs{};
|
|
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::MoveFocusFromZoomedPane()
|
|
{
|
|
auto page = _commonSetup();
|
|
|
|
Log::Comment(L"Create a second pane");
|
|
auto result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
SplitPaneArgs args{ SplitType::Duplicate };
|
|
ActionEventArgs eventArgs{ args };
|
|
page->_HandleSplitPane(nullptr, eventArgs);
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Zoom in on the pane");
|
|
result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
ActionEventArgs eventArgs{};
|
|
|
|
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
|
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Move focus. This will cause us to un-zoom.");
|
|
result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
MoveFocusArgs args{ FocusDirection::Left };
|
|
ActionEventArgs eventArgs{ args };
|
|
|
|
page->_HandleMoveFocus(nullptr, eventArgs);
|
|
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::CloseZoomedPane()
|
|
{
|
|
auto page = _commonSetup();
|
|
|
|
Log::Comment(L"Create a second pane");
|
|
auto result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
SplitPaneArgs args{ SplitType::Duplicate };
|
|
ActionEventArgs eventArgs{ args };
|
|
page->_HandleSplitPane(nullptr, eventArgs);
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Zoom in on the pane");
|
|
result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
ActionEventArgs eventArgs{};
|
|
|
|
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
|
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
Log::Comment(L"Close Pane. This should cause us to un-zoom, and remove the second pane from the tree");
|
|
result = RunOnUIThread([&page]() {
|
|
// Set up action
|
|
ActionEventArgs eventArgs{};
|
|
|
|
page->_HandleClosePane(nullptr, eventArgs);
|
|
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
|
|
// Introduce a slight delay to let the events finish propagating
|
|
Sleep(250);
|
|
|
|
Log::Comment(L"Check to ensure there's only one pane left.");
|
|
|
|
result = RunOnUIThread([&page]() {
|
|
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
|
VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount());
|
|
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
|
});
|
|
VERIFY_SUCCEEDED(result);
|
|
}
|
|
|
|
void TabTests::NextMRUTab()
|
|
{
|
|
// This is a test for GH#8025 - we want to make sure that we can do both
|
|
// in-order and MRU tab traversal, using the tab switcher and with the
|
|
// tab switcher disabled.
|
|
|
|
auto page = _commonSetup();
|
|
|
|
Log::Comment(L"Create a second tab");
|
|
TestOnUIThread([&page]() {
|
|
NewTerminalArgs newTerminalArgs{ 1 };
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
});
|
|
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
|
|
|
Log::Comment(L"Create a third tab");
|
|
TestOnUIThread([&page]() {
|
|
NewTerminalArgs newTerminalArgs{ 2 };
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
});
|
|
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
|
|
|
Log::Comment(L"Create a fourth tab");
|
|
TestOnUIThread([&page]() {
|
|
NewTerminalArgs newTerminalArgs{ 3 };
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
});
|
|
VERIFY_ARE_EQUAL(4u, page->_tabs.Size());
|
|
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
|
});
|
|
|
|
Log::Comment(L"Select the second tab");
|
|
TestOnUIThread([&page]() {
|
|
page->_SelectTab(1);
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
|
});
|
|
|
|
Log::Comment(L"Change the tab switch order to MRU switching");
|
|
TestOnUIThread([&page]() {
|
|
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
|
});
|
|
|
|
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
|
|
TestOnUIThread([&page]() {
|
|
page->_SelectNextTab(true, nullptr);
|
|
});
|
|
|
|
Log::Comment(L"Sleep to let events propagate");
|
|
Sleep(250);
|
|
|
|
TestOnUIThread([&page]() {
|
|
Log::Comment(L"Hide the command palette, to confirm the selection");
|
|
// If you don't do this, the palette will just stay open, and the
|
|
// next time we call _HandleNextTab, we'll continue traversing the
|
|
// MRU list, instead of just hoping one entry.
|
|
page->CommandPalette().Visibility(Visibility::Collapsed);
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
|
});
|
|
|
|
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
|
|
TestOnUIThread([&page]() {
|
|
page->_SelectNextTab(true, nullptr);
|
|
});
|
|
|
|
Log::Comment(L"Sleep to let events propagate");
|
|
Sleep(250);
|
|
|
|
TestOnUIThread([&page]() {
|
|
Log::Comment(L"Hide the command palette, to confirm the selection");
|
|
// If you don't do this, the palette will just stay open, and the
|
|
// next time we call _HandleNextTab, we'll continue traversing the
|
|
// MRU list, instead of just hoping one entry.
|
|
page->CommandPalette().Visibility(Visibility::Collapsed);
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
|
});
|
|
|
|
Log::Comment(L"Change the tab switch order to in-order switching");
|
|
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
|
|
|
|
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
|
|
TestOnUIThread([&page]() {
|
|
page->_SelectNextTab(true, nullptr);
|
|
});
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify the third tab is the focused one");
|
|
});
|
|
|
|
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
|
|
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
|
|
|
|
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
|
|
TestOnUIThread([&page]() {
|
|
page->_SelectNextTab(true, nullptr);
|
|
});
|
|
TestOnUIThread([&page]() {
|
|
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
|
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
|
});
|
|
}
|
|
|
|
void TabTests::VerifyCommandPaletteTabSwitcherOrder()
|
|
{
|
|
// This is a test for GH#8188 - we want to make sure that the order of tabs
|
|
// is preserved in the CommandPalette's TabSwitcher
|
|
|
|
auto page = _commonSetup();
|
|
|
|
Log::Comment(L"Create 3 additional tabs");
|
|
RunOnUIThread([&page]() {
|
|
NewTerminalArgs newTerminalArgs{ 1 };
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
page->_OpenNewTab(newTerminalArgs);
|
|
});
|
|
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
|
|
|
Log::Comment(L"give alphabetical names to all switch tab actions");
|
|
TestOnUIThread([&page]() {
|
|
page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a");
|
|
});
|
|
TestOnUIThread([&page]() {
|
|
page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b");
|
|
});
|
|
TestOnUIThread([&page]() {
|
|
page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c");
|
|
});
|
|
TestOnUIThread([&page]() {
|
|
page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d");
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
Log::Comment(L"Sanity check the titles of our tabs are what we set them to.");
|
|
|
|
VERIFY_ARE_EQUAL(L"a", page->_tabs.GetAt(0).Title());
|
|
VERIFY_ARE_EQUAL(L"b", page->_tabs.GetAt(1).Title());
|
|
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
|
|
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
|
|
|
|
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
|
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
|
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
|
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
|
});
|
|
|
|
Log::Comment(L"Change the tab switch order to MRU switching");
|
|
TestOnUIThread([&page]() {
|
|
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
|
});
|
|
|
|
Log::Comment(L"Select the tabs from 0 to 3");
|
|
RunOnUIThread([&page]() {
|
|
page->_UpdatedSelectedTab(0);
|
|
page->_UpdatedSelectedTab(1);
|
|
page->_UpdatedSelectedTab(2);
|
|
page->_UpdatedSelectedTab(3);
|
|
});
|
|
|
|
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
|
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
|
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
|
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
|
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
|
|
|
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
|
RunOnUIThread([&page]() {
|
|
page->_SelectNextTab(true, nullptr);
|
|
// In the course of a single tick, the Command Palette will:
|
|
// * open
|
|
// * select the proper tab from the mru's list
|
|
// * raise an event for _filteredActionsView().SelectionChanged to
|
|
// immediately preview the new tab
|
|
// * raise a _SwitchToTabRequestedHandlers event
|
|
// * then dismiss itself, because we can't fake holing down an
|
|
// anchor key in the tests
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
|
|
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
|
|
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
|
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
|
});
|
|
|
|
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->CommandPalette());
|
|
|
|
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
|
// At this point, the contents of the command palette's _mruTabs list is
|
|
// still the _old_ ordering (d, c, b, a). The ordering is only updated
|
|
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
|
|
// will also dismiss itself immediately when that's called. So we can't
|
|
// really inspect the contents of the list in this test, unfortunately.
|
|
}
|
|
|
|
void TabTests::TestWindowRenameSuccessful()
|
|
{
|
|
auto page = _commonSetup();
|
|
page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
|
|
// In the real terminal, this would bounce up to the monarch and
|
|
// come back down. Instead, immediately call back and set the name.
|
|
page->WindowName(args.ProposedName());
|
|
});
|
|
|
|
bool windowNameChanged = false;
|
|
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
|
if (args.PropertyName() == L"WindowNameForDisplay")
|
|
{
|
|
windowNameChanged = true;
|
|
}
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
|
|
});
|
|
TestOnUIThread([&]() {
|
|
VERIFY_ARE_EQUAL(L"Foo", page->_WindowName);
|
|
VERIFY_IS_TRUE(windowNameChanged,
|
|
L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed");
|
|
});
|
|
}
|
|
void TabTests::TestWindowRenameFailure()
|
|
{
|
|
auto page = _commonSetup();
|
|
page->RenameWindowRequested([&page](auto&&, auto&&) {
|
|
// In the real terminal, this would bounce up to the monarch and
|
|
// come back down. Instead, immediately call back to tell the terminal it failed.
|
|
page->RenameFailed();
|
|
});
|
|
|
|
bool windowNameChanged = false;
|
|
|
|
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
|
if (args.PropertyName() == L"WindowNameForDisplay")
|
|
{
|
|
windowNameChanged = true;
|
|
}
|
|
});
|
|
|
|
TestOnUIThread([&page]() {
|
|
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
|
|
});
|
|
TestOnUIThread([&]() {
|
|
VERIFY_IS_FALSE(windowNameChanged,
|
|
L"The window name should not have changed, we should have rejected the change.");
|
|
});
|
|
}
|
|
}
|