OSDN Git Service

起動時にタブ設定を読み込むタイミングを変更
[opentween/open-tween.git] / OpenTween.Tests / TweenMainTest.cs
index e7a8b4f..526fbf0 100644 (file)
@@ -23,9 +23,16 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Text;
+using System.Text.RegularExpressions;
 using System.Windows.Forms;
-using OpenTween.Api;
+using OpenTween.Api.DataModel;
+using OpenTween.Models;
+using OpenTween.OpenTweenCustomControl;
+using OpenTween.Setting;
+using OpenTween.SocialProtocol;
+using OpenTween.Thumbnail;
 using Xunit;
 using Xunit.Extensions;
 
@@ -33,60 +40,464 @@ namespace OpenTween
 {
     public class TweenMainTest
     {
+        private record TestContext(
+            SettingManager Settings,
+            TabInformations TabInfo
+        );
+
+        private void UsingTweenMain(Action<TweenMain, TestContext> func)
+        {
+            var settings = new SettingManager("");
+            var tabinfo = new TabInformations();
+            using var accounts = new AccountCollection();
+            using var imageCache = new ImageCache();
+            using var iconAssets = new IconAssetsManager();
+            var thumbnailGenerator = new ThumbnailGenerator(new(autoupdate: false));
+
+            // TabInformation.GetInstance() で取得できるようにする
+            var field = typeof(TabInformations).GetField("Instance",
+                BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.SetField);
+            field.SetValue(null, tabinfo);
+
+            tabinfo.AddDefaultTabs();
+
+            using var tweenMain = new TweenMain(settings, tabinfo, accounts, imageCache, iconAssets, thumbnailGenerator);
+            var context = new TestContext(settings, tabinfo);
+
+            func(tweenMain, context);
+        }
+
+        [WinFormsFact]
+        public void Initialize_Test()
+            => this.UsingTweenMain((_, _) => { });
+
+        [WinFormsFact]
+        public void AddNewTab_FilterTabTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+
+                var tab = new FilterTabModel("hoge");
+                context.TabInfo.AddTab(tab);
+                tweenMain.AddNewTab(tab, startup: false);
+
+                Assert.Equal(5, tweenMain.ListTab.TabPages.Count);
+
+                var tabPage = tweenMain.ListTab.TabPages[4];
+                Assert.Equal("hoge", tabPage.Text);
+                Assert.Single(tabPage.Controls);
+                Assert.IsType<DetailsListView>(tabPage.Controls[0]);
+            });
+        }
+
+        [WinFormsFact]
+        public void AddNewTab_UserTimelineTabTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+
+                var tab = new UserTimelineTabModel("hoge", "twitterapi");
+                context.TabInfo.AddTab(tab);
+                tweenMain.AddNewTab(tab, startup: false);
+
+                Assert.Equal(5, tweenMain.ListTab.TabPages.Count);
+
+                var tabPage = tweenMain.ListTab.TabPages[4];
+                Assert.Equal("hoge", tabPage.Text);
+                Assert.Equal(2, tabPage.Controls.Count);
+                Assert.IsType<DetailsListView>(tabPage.Controls[0]);
+
+                var label = Assert.IsType<Label>(tabPage.Controls[1]);
+                Assert.Equal("twitterapi's Timeline", label.Text);
+            });
+        }
+
+        [WinFormsFact]
+        public void AddNewTab_ListTimelineTabTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+
+                var list = new ListElement
+                {
+                    Id = 12345L,
+                    Name = "tetete",
+                    Username = "opentween",
+                    IsPublic = false,
+                };
+                var tab = new ListTimelineTabModel("hoge", list);
+                context.TabInfo.AddTab(tab);
+                tweenMain.AddNewTab(tab, startup: false);
+
+                Assert.Equal(5, tweenMain.ListTab.TabPages.Count);
+
+                var tabPage = tweenMain.ListTab.TabPages[4];
+                Assert.Equal("hoge", tabPage.Text);
+                Assert.Equal(2, tabPage.Controls.Count);
+                Assert.IsType<DetailsListView>(tabPage.Controls[0]);
+
+                var label = Assert.IsType<Label>(tabPage.Controls[1]);
+                Assert.Equal("@opentween/tetete [Protected]", label.Text);
+            });
+        }
+
+        [WinFormsFact]
+        public void AddNewTab_PublicSearchTabTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+
+                var tab = new PublicSearchTabModel("hoge")
+                {
+                    SearchWords = "#OpenTween",
+                    SearchLang = "ja",
+                };
+                context.TabInfo.AddTab(tab);
+                tweenMain.AddNewTab(tab, startup: false);
+
+                Assert.Equal(5, tweenMain.ListTab.TabPages.Count);
+
+                var tabPage = tweenMain.ListTab.TabPages[4];
+                Assert.Equal("hoge", tabPage.Text);
+                Assert.Equal(2, tabPage.Controls.Count);
+                Assert.IsType<DetailsListView>(tabPage.Controls[0]);
+
+                var panel = Assert.IsType<Panel>(tabPage.Controls[1]);
+                Assert.Equal(4, panel.Controls.Count);
+
+                var comboSearchWord = Assert.IsType<ComboBox>(panel.Controls[0]);
+                Assert.Equal("#OpenTween", comboSearchWord.Text);
+
+                var comboSearchLang = Assert.IsType<ComboBox>(panel.Controls[1]);
+                Assert.Equal("ja", comboSearchLang.Text);
+
+                Assert.IsType<Button>(panel.Controls[2]);
+                Assert.IsType<Label>(panel.Controls[3]);
+            });
+        }
+
+        [WinFormsFact]
+        public void RemoveSpecifiedTab_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+
+                var tab = new PublicSearchTabModel("hoge")
+                {
+                    SearchWords = "#OpenTween",
+                    SearchLang = "ja",
+                };
+                context.TabInfo.AddTab(tab);
+                tweenMain.AddNewTab(tab, startup: false);
+                Assert.Equal(5, tweenMain.ListTab.TabPages.Count);
+
+                var tabPage = tweenMain.ListTab.TabPages[4];
+                var listView = (DetailsListView)tabPage.Controls[0];
+                var searchPanel = (Panel)tabPage.Controls[1];
+                Assert.Equal("hoge", tabPage.Text);
+
+                tweenMain.RemoveSpecifiedTab("hoge", confirm: false);
+
+                Assert.Equal(4, tweenMain.ListTab.TabPages.Count);
+                Assert.False(context.TabInfo.ContainsTab("hoge"));
+                Assert.True(tabPage.IsDisposed);
+                Assert.True(listView.IsDisposed);
+                Assert.True(searchPanel.IsDisposed);
+            });
+        }
+
+        [WinFormsFact]
+        public void RefreshTimeline_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                var tabPage = tweenMain.ListTab.TabPages[0];
+                Assert.Equal("Recent", tabPage.Text);
+
+                var listView = (DetailsListView)tabPage.Controls[0];
+                Assert.Equal(0, listView.VirtualListSize);
+
+                var post = new PostClass
+                {
+                    StatusId = new TwitterStatusId("100"),
+                    Text = "hoge",
+                    UserId = 111L,
+                    ScreenName = "opentween",
+                    CreatedAt = new(2024, 1, 1, 0, 0, 0),
+                };
+                context.TabInfo.AddPost(post);
+                context.TabInfo.DistributePosts();
+                tweenMain.RefreshTimeline();
+
+                Assert.Equal(1, listView.VirtualListSize);
+
+                var listItem = listView.Items[0];
+                Assert.Equal("opentween", listItem.SubItems[4].Text);
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_NewLineTest()
+        {
+            this.UsingTweenMain((tweenMain, _) =>
+            {
+                Assert.Equal("aaa\nbbb", tweenMain.FormatStatusText("aaa\r\nbbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_NewLineInDMTest()
+        {
+            this.UsingTweenMain((tweenMain, _) =>
+            {
+                // DM にも適用する
+                Assert.Equal("D opentween aaa\nbbb", tweenMain.FormatStatusText("D opentween aaa\r\nbbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_SeparateUrlAndFullwidthCharacter_EnabledTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                tweenMain.SeparateUrlAndFullwidthCharacter = true;
+                Assert.Equal("https://example.com/ あああ", tweenMain.FormatStatusText("https://example.com/あああ"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_SeparateUrlAndFullwidthCharacter_DisabledTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                tweenMain.SeparateUrlAndFullwidthCharacter = false;
+                Assert.Equal("https://example.com/あああ", tweenMain.FormatStatusText("https://example.com/あああ"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_ReplaceFullwidthSpace_EnabledTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.WideSpaceConvert = true;
+                Assert.Equal("aaa bbb", tweenMain.FormatStatusText("aaa bbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_ReplaceFullwidthSpaceInDM_EnabledTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.WideSpaceConvert = true;
+
+                // DM にも適用する
+                Assert.Equal("D opentween aaa bbb", tweenMain.FormatStatusText("D opentween aaa bbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_ReplaceFullwidthSpace_DisabledTest()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.WideSpaceConvert = false;
+                Assert.Equal("aaa bbb", tweenMain.FormatStatusText("aaa bbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_UseRecommendedFooter_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Local.UseRecommendStatus = true;
+                Assert.Matches(new Regex(@"^aaa \[TWNv\d+\]$"), tweenMain.FormatStatusText("aaa"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_CustomFooterText_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Local.StatusText = "foo";
+                Assert.Equal("aaa foo", tweenMain.FormatStatusText("aaa"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfSendingDM_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Local.StatusText = "foo";
+
+                // DM の場合はフッターを無効化する
+                Assert.Equal("D opentween aaa", tweenMain.FormatStatusText("D opentween aaa"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfContainsUnofficialRT_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Local.StatusText = "foo";
+
+                // 非公式 RT を含む場合はフッターを無効化する
+                Assert.Equal("aaa RT @foo: bbb", tweenMain.FormatStatusText("aaa RT @foo: bbb"));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfPostByEnterAndPressedShiftKey_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = false;
+                context.Settings.Common.PostShiftEnter = false; // Enter で投稿する設定
+                context.Settings.Local.StatusText = "foo";
+                context.Settings.Local.StatusMultiline = false; // 単一行モード
+
+                // Shift キーが押されている場合はフッターを無効化する
+                Assert.Equal("aaa", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Shift));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfPostByEnterAndPressedCtrlKeyAndMultilineMode_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = false;
+                context.Settings.Common.PostShiftEnter = false; // Enter で投稿する設定
+                context.Settings.Local.StatusText = "foo";
+                context.Settings.Local.StatusMultiline = true; // 複数行モード
+
+                // Ctrl キーが押されている場合はフッターを無効化する
+                Assert.Equal("aaa", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Control));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfPostByShiftEnterAndPressedControlKey_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = false;
+                context.Settings.Common.PostShiftEnter = true; // Shift+Enter で投稿する設定
+                context.Settings.Local.StatusText = "foo";
+
+                // Ctrl キーが押されている場合はフッターを無効化する
+                Assert.Equal("aaa", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Control));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_EnableFooterIfPostByShiftEnter_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = false;
+                context.Settings.Common.PostShiftEnter = true; // Shift+Enter で投稿する設定
+                context.Settings.Local.StatusText = "foo";
+
+                // Shift+Enter で投稿する場合、Ctrl キーが押されていなければフッターを付ける
+                Assert.Equal("aaa foo", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Shift));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_DisableFooterIfPostByCtrlEnterAndPressedShiftKey_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = true; // Ctrl+Enter で投稿する設定
+                context.Settings.Common.PostShiftEnter = false;
+                context.Settings.Local.StatusText = "foo";
+
+                // Shift キーが押されている場合はフッターを無効化する
+                Assert.Equal("aaa", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Shift));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_EnableFooterIfPostByCtrlEnter_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                context.Settings.Common.PostCtrlEnter = true; // Ctrl+Enter で投稿する設定
+                context.Settings.Common.PostShiftEnter = false;
+                context.Settings.Local.StatusText = "foo";
+
+                // Ctrl+Enter で投稿する場合、Shift キーが押されていなければフッターを付ける
+                Assert.Equal("aaa foo", tweenMain.FormatStatusText("aaa", modifierKeys: Keys.Control));
+            });
+        }
+
+        [WinFormsFact]
+        public void FormatStatusText_PreventSmsCommand_Test()
+        {
+            this.UsingTweenMain((tweenMain, context) =>
+            {
+                // 「D+」などから始まる文字列をツイートしようとすると SMS コマンドと誤認されてエラーが返される問題の回避策
+                Assert.Equal("\u200bd+aaaa", tweenMain.FormatStatusText("d+aaaa"));
+            });
+        }
+
         [Fact]
         public void GetUrlFromDataObject_XMozUrlTest()
         {
             var dataBytes = Encoding.Unicode.GetBytes("https://twitter.com/\nTwitter\0");
-            using (var memstream = new MemoryStream(dataBytes))
-            {
-                var data = new DataObject("text/x-moz-url", memstream);
+            using var memstream = new MemoryStream(dataBytes);
+            var data = new DataObject("text/x-moz-url", memstream);
 
-                var expected = new Tuple<string, string>("https://twitter.com/", "Twitter");
-                Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
-            }
+            var expected = ("https://twitter.com/", "Twitter");
+            Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
         }
 
         [Fact]
         public void GetUrlFromDataObject_IESiteModeToUrlTest()
         {
             var dataBytes = Encoding.Unicode.GetBytes("https://twitter.com/\0Twitter\0");
-            using (var memstream = new MemoryStream(dataBytes))
-            {
-                var data = new DataObject("IESiteModeToUrl", memstream);
+            using var memstream = new MemoryStream(dataBytes);
+            var data = new DataObject("IESiteModeToUrl", memstream);
 
-                var expected = new Tuple<string, string>("https://twitter.com/", "Twitter");
-                Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
-            }
+            var expected = ("https://twitter.com/", "Twitter");
+            Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
         }
 
         [Fact]
         public void GetUrlFromDataObject_UniformResourceLocatorWTest()
         {
             var dataBytes = Encoding.Unicode.GetBytes("https://twitter.com/\0");
-            using (var memstream = new MemoryStream(dataBytes))
-            {
-                var data = new DataObject("UniformResourceLocatorW", memstream);
+            using var memstream = new MemoryStream(dataBytes);
+            var data = new DataObject("UniformResourceLocatorW", memstream);
 
-                var expected = new Tuple<string, string>("https://twitter.com/", null);
-                Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
-            }
+            var expected = ("https://twitter.com/", (string?)null);
+            Assert.Equal(expected, TweenMain.GetUrlFromDataObject(data));
         }
 
         [Fact]
         public void GetUrlFromDataObject_UnknownFormatTest()
         {
-            using (var memstream = new MemoryStream(new byte[0]))
-            {
-                var data = new DataObject("application/x-hogehoge", memstream);
+            using var memstream = new MemoryStream(Array.Empty<byte>());
+            var data = new DataObject("application/x-hogehoge", memstream);
 
-                Assert.Throws<NotSupportedException>(() => TweenMain.GetUrlFromDataObject(data));
-            }
+            Assert.Throws<NotSupportedException>(() => TweenMain.GetUrlFromDataObject(data));
         }
 
         [Fact]
         public void CreateRetweetUnofficial_UrlTest()
         {
-            var statusText = "<a href=\"http://t.co/KYi7vMZzRt\" title=\"http://twitter.com/\">twitter.com</a>";
+            var statusText = """<a href="http://t.co/KYi7vMZzRt" title="http://twitter.com/">twitter.com</a>""";
 
             Assert.Equal("http://twitter.com/", TweenMain.CreateRetweetUnofficial(statusText, false));
         }
@@ -94,7 +505,7 @@ namespace OpenTween
         [Fact]
         public void CreateRetweetUnofficial_MentionTest()
         {
-            var statusText = "<a class=\"mention\" href=\"https://twitter.com/twitterapi\">@TwitterAPI</a>";
+            var statusText = """<a class="mention" href="https://twitter.com/twitterapi">@TwitterAPI</a>""";
 
             Assert.Equal("@TwitterAPI", TweenMain.CreateRetweetUnofficial(statusText, false));
         }
@@ -102,7 +513,7 @@ namespace OpenTween
         [Fact]
         public void CreateRetweetUnofficial_HashtagTest()
         {
-            var statusText = "<a class=\"hashtag\" href=\"https://twitter.com/search?q=%23OpenTween\">#OpenTween</a>";
+            var statusText = """<a class="hashtag" href="https://twitter.com/search?q=%23OpenTween">#OpenTween</a>""";
 
             Assert.Equal("#OpenTween", TweenMain.CreateRetweetUnofficial(statusText, false));
         }
@@ -166,57 +577,13 @@ namespace OpenTween
             Assert.Equal(expected, TweenMain.CreateRetweetUnofficial(html, true));
         }
 
-        [Fact]
-        public void FormatQuoteTweetHtml_PostClassTest()
-        {
-            var post = new PostClass
-            {
-                StatusId = 12345L,
-                Nickname = "upsilon",
-                ScreenName = "kim_upsilon",
-                Text = "<a href=\"https://twitter.com/twitterapi\">@twitterapi</a> hogehoge",
-                CreatedAt = new DateTime(2015, 3, 30, 3, 30, 0),
-            };
-
-            // PostClass.Text はリンクを除去するのみでエスケープは行わない
-            // (TweetFormatter によって既にエスケープされた文字列が格納されているため)
-
-            var expected = "<a class=\"quote-tweet-link\" href=\"//opentween/status/12345\">" +
-                "<blockquote class=\"quote-tweet\">" +
-                "<p>@twitterapi hogehoge</p> &mdash; upsilon (@kim_upsilon) " + DateTime.Parse("2015/03/30 3:30:00") +
-                "</blockquote></a>";
-            Assert.Equal(expected, TweenMain.FormatQuoteTweetHtml(post, isReply: false));
-        }
-
-        [Fact]
-        public void FormatQuoteTweetHtml_HtmlTest()
-        {
-            var statusId = 12345L; // リンク先のステータスID
-            var html = "<marquee>hogehoge</marquee>"; // HTMLをそのまま出力する (エスケープしない)
-
-            var expected = "<a class=\"quote-tweet-link\" href=\"//opentween/status/12345\">" +
-                "<blockquote class=\"quote-tweet\"><marquee>hogehoge</marquee></blockquote>" +
-                "</a>";
-            Assert.Equal(expected, TweenMain.FormatQuoteTweetHtml(statusId, html, isReply: false));
-        }
-
-        [Fact]
-        public void FormatQuoteTweetHtml_ReplyHtmlTest()
-        {
-            // blockquote の class に reply が付与される
-            var expected = "<a class=\"quote-tweet-link\" href=\"//opentween/status/12345\">" +
-                "<blockquote class=\"quote-tweet reply\">hogehoge</blockquote>" +
-                "</a>";
-            Assert.Equal(expected, TweenMain.FormatQuoteTweetHtml(12345L, "hogehoge", isReply: true));
-        }
-
-        [Fact]
-        public void StripLinkTagHtml_Test()
-        {
-            var html = "<a href=\"https://twitter.com/twitterapi\">@twitterapi</a>";
-
-            var expected = "@twitterapi";
-            Assert.Equal(expected, TweenMain.StripLinkTagHtml(html));
-        }
+        [Theory]
+        [InlineData("", true)]
+        [InlineData("hoge", false)]
+        [InlineData("@twitterapi ", true)]
+        [InlineData("@twitterapi @opentween ", true)]
+        [InlineData("@twitterapi @opentween hoge", false)]
+        public void TextContainsOnlyMentions_Test(string input, bool expected)
+            => Assert.Equal(expected, TweenMain.TextContainsOnlyMentions(input));
     }
 }