4f46129cb4
## Summary of the Pull Request Adds a `size` parameter to `splitPane`. This takes a `float`, and specifies the portion of the parent pane that should be used to create the new one. This also adds the param to the `split-pane` subcommand. ### Examples | commandline | result | | -- | -- | | `wt ; sp -s .25` | ![image](https://user-images.githubusercontent.com/18356694/101784317-fb595680-3ac0-11eb-8248-782dc61957cf.png) | | `wt ; sp -s .8` | ![image](https://user-images.githubusercontent.com/18356694/101784442-20e66000-3ac1-11eb-8f9b-fb45a73c9334.png) | | `wt ; sp -s .8 ; sp -H -s .3` | ![image](https://user-images.githubusercontent.com/18356694/101784552-470c0000-3ac1-11eb-9deb-df37aaa36f01.png) | ## PR Checklist * [x] Closes #6298 * [x] I work here * [x] Tests added/passed * [x] Docs PR: MicrosoftDocs/terminal#208 ## Detailed Description of the Pull Request / Additional comments I went with `size`, `--size,-s` rather than `percent`, because the arg is the (0,1) version of the size, not the (0%,100%) version. ## Validation Steps Performed Added actions, played with the commandline, ran tests
373 lines
18 KiB
C++
373 lines
18 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
|
|
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
|
#include "JsonTestClass.h"
|
|
#include "TestUtils.h"
|
|
|
|
using namespace Microsoft::Console;
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
|
using namespace winrt::Windows::Foundation::Collections;
|
|
using namespace WEX::Logging;
|
|
using namespace WEX::TestExecution;
|
|
using namespace WEX::Common;
|
|
|
|
namespace SettingsModelLocalTests
|
|
{
|
|
// 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 CommandTests : public JsonTestClass
|
|
{
|
|
// Use a custom AppxManifest to ensure that we can activate winrt types
|
|
// from our test. This property will tell taef to manually use this as
|
|
// the AppxManifest for this test class.
|
|
// This does not yet work for anything XAML-y. See TabTests.cpp for more
|
|
// details on that.
|
|
BEGIN_TEST_CLASS(CommandTests)
|
|
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
|
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
|
END_TEST_CLASS()
|
|
|
|
TEST_METHOD(ManyCommandsSameAction);
|
|
TEST_METHOD(LayerCommand);
|
|
TEST_METHOD(TestSplitPaneArgs);
|
|
TEST_METHOD(TestSplitPaneBadSize);
|
|
TEST_METHOD(TestResourceKeyName);
|
|
TEST_METHOD(TestAutogeneratedName);
|
|
TEST_METHOD(TestLayerOnAutogeneratedName);
|
|
|
|
TEST_CLASS_SETUP(ClassSetup)
|
|
{
|
|
InitializeJsonReader();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
void CommandTests::ManyCommandsSameAction()
|
|
{
|
|
const std::string commands0String{ R"([ { "name":"action0", "command": "copy" } ])" };
|
|
const std::string commands1String{ R"([ { "name":"action1", "command": { "action": "copy", "singleLine": false } } ])" };
|
|
const std::string commands2String{ R"([
|
|
{ "name":"action2", "command": "paste" },
|
|
{ "name":"action3", "command": "paste" }
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
const auto commands1Json = VerifyParseSucceeded(commands1String);
|
|
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands1Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(2u, commands.Size());
|
|
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
}
|
|
VERIFY_ARE_EQUAL(4u, commands.Size());
|
|
}
|
|
|
|
void CommandTests::LayerCommand()
|
|
{
|
|
// Each one of the commands in this test should layer upon the previous, overriding the action.
|
|
const std::string commands0String{ R"([ { "name":"action0", "command": "copy" } ])" };
|
|
const std::string commands1String{ R"([ { "name":"action0", "command": "paste" } ])" };
|
|
const std::string commands2String{ R"([ { "name":"action0", "command": "newTab" } ])" };
|
|
const std::string commands3String{ R"([ { "name":"action0", "command": null } ])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
const auto commands1Json = VerifyParseSucceeded(commands1String);
|
|
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
|
const auto commands3Json = VerifyParseSucceeded(commands3String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands1Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.Action().Action());
|
|
VERIFY_IS_NULL(command.Action().Args());
|
|
}
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
auto command = commands.Lookup(L"action0");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
{
|
|
// This last command should "unbind" the action.
|
|
auto warnings = implementation::Command::LayerJson(commands, commands3Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestSplitPaneArgs()
|
|
{
|
|
// This is the same as KeyBindingsTests::TestSplitPaneArgs, but with
|
|
// looking up the action and its args from a map of commands, instead
|
|
// of from keybindings.
|
|
|
|
const std::string commands0String{ R"([
|
|
{ "name": "command1", "command": { "action": "splitPane", "split": "vertical" } },
|
|
{ "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } },
|
|
{ "name": "command4", "command": { "action": "splitPane" } },
|
|
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
|
|
{ "name": "command6", "command": { "action": "splitPane", "size": 0.25 } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(5u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"command1");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command2");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command4");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command5");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"command6");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestSplitPaneBadSize()
|
|
{
|
|
const std::string commands0String{ R"([
|
|
{ "name": "command1", "command": { "action": "splitPane", "size": 0.25 } },
|
|
{ "name": "command2", "command": { "action": "splitPane", "size": 1.0 } },
|
|
{ "name": "command3", "command": { "action": "splitPane", "size": 0 } },
|
|
{ "name": "command4", "command": { "action": "splitPane", "size": 50 } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(3u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"command1");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
|
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestResourceKeyName()
|
|
{
|
|
// This test checks looking up a name from a resource key.
|
|
|
|
const std::string commands0String{ R"([ { "name": { "key": "DuplicateTabCommandKey"}, "command": "copy" } ])" };
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
{
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
// NOTE: We're relying on DuplicateTabCommandKey being defined as
|
|
// "Duplicate Tab" here. If that string changes in our resources,
|
|
// this test will break.
|
|
auto command = commands.Lookup(L"Duplicate tab");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
}
|
|
}
|
|
|
|
void CommandTests::TestAutogeneratedName()
|
|
{
|
|
// Tests run in Helix can't report Skipped until GH#7286 is resolved.
|
|
// Set ignore flag to make Helix run completely overlook it.
|
|
BEGIN_TEST_METHOD_PROPERTIES()
|
|
TEST_METHOD_PROPERTY(L"Ignore", L"True")
|
|
END_TEST_METHOD_PROPERTIES()
|
|
|
|
// This test to be corrected as a part of GH#7281
|
|
|
|
// This test ensures that we'll correctly create commands for actions
|
|
// that don't have given names, pursuant to the spec in GH#6532.
|
|
|
|
// NOTE: The keys used to look up these commands are partially generated
|
|
// from strings in our Resources.resw. If those string values should
|
|
// change, it's likely that this test will break.
|
|
|
|
const std::string commands0String{ R"([
|
|
{ "command": { "action": "splitPane", "split": null } },
|
|
{ "command": { "action": "splitPane", "split": "vertical" } },
|
|
{ "command": { "action": "splitPane", "split": "horizontal" } },
|
|
{ "command": { "action": "splitPane", "split": "none" } },
|
|
{ "command": { "action": "splitPane" } },
|
|
{ "command": { "action": "splitPane", "split": "auto" } },
|
|
{ "command": { "action": "splitPane", "split": "foo" } }
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
|
|
// There are only 3 commands here: all of the `"none"`, `"auto"`,
|
|
// `"foo"`, `null`, and <no args> bindings all generate the same action,
|
|
// which will generate just a single name for all of them.
|
|
VERIFY_ARE_EQUAL(3u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"Split pane");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: vertical");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
|
}
|
|
{
|
|
auto command = commands.Lookup(L"Split pane, split: horizontal");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
|
}
|
|
}
|
|
void CommandTests::TestLayerOnAutogeneratedName()
|
|
{
|
|
const std::string commands0String{ R"([
|
|
{ "command": { "action": "splitPane" } },
|
|
{ "name":"Split pane", "command": { "action": "splitPane", "split": "vertical" } },
|
|
])" };
|
|
|
|
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
|
|
|
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
|
VERIFY_ARE_EQUAL(0u, commands.Size());
|
|
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
|
VERIFY_ARE_EQUAL(0u, warnings.size());
|
|
VERIFY_ARE_EQUAL(1u, commands.Size());
|
|
|
|
{
|
|
auto command = commands.Lookup(L"Split pane");
|
|
VERIFY_IS_NOT_NULL(command);
|
|
VERIFY_IS_NOT_NULL(command.Action());
|
|
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
|
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
|
VERIFY_IS_NOT_NULL(realArgs);
|
|
// Verify the args have the expected value
|
|
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
|
}
|
|
}
|
|
}
|