mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 01:32:10 +02:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
52f1f4c4eb | |||
6c1782a038 | |||
8b8f5f5473 | |||
61d3ed891a | |||
b1abf87320 | |||
9aedfc2799 | |||
ad6240a067 | |||
9539eb076a | |||
c808e7bd83 | |||
13ea388f5e | |||
c46dc0f1a3 | |||
2ae311007d | |||
9344e02bff | |||
40ad836fc3 | |||
e8604a261d | |||
2a41d21a29 | |||
4c62aa067b | |||
49db3074c6 | |||
f5e3b34f30 | |||
f0affa4aec | |||
4f5075ac54 | |||
20f0445b10 | |||
c77c974455 | |||
44397b2d45 |
@@ -105,7 +105,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_muteNotifications != value){
|
||||
_muteNotifications = value;
|
||||
MuteToggled?.Invoke(this, new EventArgs());
|
||||
MuteToggled?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_zoomLevel != value){
|
||||
_zoomLevel = value;
|
||||
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
||||
ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_trayBehavior != value){
|
||||
_trayBehavior = value;
|
||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
||||
TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,15 +11,18 @@ using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Bridge{
|
||||
sealed class TweetDeckBridge{
|
||||
public static string LastHighlightedTweet = string.Empty;
|
||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||
public static string LastHighlightedTweetAuthor = string.Empty;
|
||||
public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray;
|
||||
public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||
public static string LastHighlightedTweetUrl = string.Empty;
|
||||
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||
private static string LastHighlightedTweetAuthors = string.Empty;
|
||||
private static string LastHighlightedTweetImages = string.Empty;
|
||||
|
||||
public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
|
||||
public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
|
||||
|
||||
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||
|
||||
public static void ResetStaticProperties(){
|
||||
LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty;
|
||||
LastHighlightedTweetImages = StringUtils.EmptyArray;
|
||||
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||
}
|
||||
|
||||
public static void RestoreSessionData(IFrame frame){
|
||||
@@ -59,12 +62,12 @@ namespace TweetDuck.Core.Bridge{
|
||||
form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
|
||||
}
|
||||
|
||||
public void SetLastHighlightedTweet(string link, string quotedLink, string author, string imageList){
|
||||
public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
LastHighlightedTweet = link;
|
||||
LastHighlightedQuotedTweet = quotedLink;
|
||||
LastHighlightedTweetAuthor = author;
|
||||
LastHighlightedTweetImages = imageList.Split(';');
|
||||
LastHighlightedTweetUrl = tweetUrl;
|
||||
LastHighlightedQuoteUrl = quoteUrl;
|
||||
LastHighlightedTweetAuthors = authors;
|
||||
LastHighlightedTweetImages = imageList;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,10 +75,10 @@ namespace TweetDuck.Core.Bridge{
|
||||
form.InvokeAsyncSafe(form.OpenContextMenu);
|
||||
}
|
||||
|
||||
public void OnTweetPopup(string columnKey, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||
notification.InvokeAsyncSafe(() => {
|
||||
form.OnTweetNotification();
|
||||
notification.ShowNotification(new TweetNotification(columnKey, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||
notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,12 +120,12 @@ namespace TweetDuck.Core.Bridge{
|
||||
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||
}
|
||||
|
||||
public int GetIdleSeconds(){
|
||||
return NativeMethods.GetIdleSeconds();
|
||||
public void OpenBrowser(string url){
|
||||
form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||
}
|
||||
|
||||
public void OpenBrowser(string url){
|
||||
BrowserUtils.OpenExternalBrowser(url);
|
||||
public int GetIdleSeconds(){
|
||||
return NativeMethods.GetIdleSeconds();
|
||||
}
|
||||
|
||||
public void Alert(string type, string contents){
|
||||
|
@@ -84,6 +84,7 @@ namespace TweetDuck.Core{
|
||||
this.notification.Show();
|
||||
|
||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||
DialogHandler = new FileDialogHandler(),
|
||||
MenuHandler = new ContextMenuBrowser(this),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
||||
@@ -377,7 +378,7 @@ namespace TweetDuck.Core{
|
||||
if (isLoaded){
|
||||
if (m.Msg == Program.WindowRestoreMessage){
|
||||
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
||||
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@@ -7,6 +7,7 @@ using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
abstract class ContextMenuBase : IContextMenuHandler{
|
||||
@@ -35,25 +36,19 @@ namespace TweetDuck.Core.Handling{
|
||||
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505;
|
||||
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506;
|
||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||
|
||||
private readonly Form form;
|
||||
|
||||
private string lastHighlightedTweetAuthor;
|
||||
private string[] lastHighlightedTweetAuthors;
|
||||
private string[] lastHighlightedTweetImageList;
|
||||
|
||||
protected ContextMenuBase(Form form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweetAuthor = string.Empty;
|
||||
lastHighlightedTweetAuthors = StringUtils.EmptyArray;
|
||||
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
||||
ContextInfo = default(KeyValuePair<string, string>);
|
||||
}
|
||||
else{
|
||||
lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor;
|
||||
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
|
||||
lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
|
||||
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
|
||||
}
|
||||
|
||||
bool hasTweetImage = IsImage;
|
||||
@@ -99,24 +94,24 @@ namespace TweetDuck.Core.Handling{
|
||||
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||
switch(commandId){
|
||||
case MenuOpenLinkUrl:
|
||||
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
||||
OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl);
|
||||
break;
|
||||
|
||||
case MenuCopyLinkUrl:
|
||||
SetClipboardText(IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
||||
SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
||||
break;
|
||||
|
||||
case MenuCopyUsername:
|
||||
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
||||
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||
SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||
break;
|
||||
|
||||
case MenuOpenMediaUrl:
|
||||
BrowserUtils.OpenExternalBrowser(TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||
OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||
break;
|
||||
|
||||
case MenuCopyMediaUrl:
|
||||
SetClipboardText(TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||
break;
|
||||
|
||||
case MenuSaveMedia:
|
||||
@@ -124,13 +119,13 @@ namespace TweetDuck.Core.Handling{
|
||||
TwitterUtils.DownloadVideo(GetMediaLink(parameters));
|
||||
}
|
||||
else{
|
||||
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthor, ImageQuality);
|
||||
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MenuSaveTweetImages:
|
||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality);
|
||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||
break;
|
||||
|
||||
case MenuOpenDevTools:
|
||||
@@ -149,8 +144,12 @@ namespace TweetDuck.Core.Handling{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void SetClipboardText(string text){
|
||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||
protected void OpenBrowser(Control control, string url){
|
||||
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||
}
|
||||
|
||||
protected void SetClipboardText(Control control, string text){
|
||||
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||
}
|
||||
|
||||
protected static void AddDebugMenuItems(IMenuModel model){
|
||||
|
@@ -26,10 +26,10 @@ namespace TweetDuck.Core.Handling{
|
||||
|
||||
private readonly FormBrowser form;
|
||||
|
||||
private string lastHighlightedTweet;
|
||||
private string lastHighlightedQuotedTweet;
|
||||
private string lastHighlightedTweetUrl;
|
||||
private string lastHighlightedQuoteUrl;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||
public ContextMenuBrowser(FormBrowser form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
@@ -46,20 +46,20 @@ namespace TweetDuck.Core.Handling{
|
||||
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||
lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
|
||||
lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
|
||||
|
||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweet = string.Empty;
|
||||
lastHighlightedQuotedTweet = string.Empty;
|
||||
lastHighlightedTweetUrl = string.Empty;
|
||||
lastHighlightedQuoteUrl = string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
|
||||
model.AddSeparator();
|
||||
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
@@ -118,11 +118,11 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuOpenTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet);
|
||||
OpenBrowser(form, lastHighlightedTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
SetClipboardText(lastHighlightedTweet);
|
||||
SetClipboardText(form, lastHighlightedTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuScreenshotTweet:
|
||||
@@ -130,11 +130,11 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuOpenQuotedTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet);
|
||||
OpenBrowser(form, lastHighlightedQuoteUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(lastHighlightedQuotedTweet);
|
||||
SetClipboardText(form, lastHighlightedQuoteUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Handling{
|
||||
private readonly FormNotificationBase form;
|
||||
private readonly bool enableCustomMenu;
|
||||
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu){
|
||||
this.form = form;
|
||||
this.enableCustomMenu = enableCustomMenu;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace TweetDuck.Core.Handling{
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
if (enableCustomMenu){
|
||||
if (!string.IsNullOrEmpty(form.CurrentChirpId)){
|
||||
if (form.CanViewDetail){
|
||||
model.AddItem(MenuViewDetail, "View detail");
|
||||
}
|
||||
|
||||
@@ -76,11 +76,11 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
SetClipboardText(form.CurrentTweetUrl);
|
||||
SetClipboardText(form, form.CurrentTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(form.CurrentQuoteUrl);
|
||||
SetClipboardText(form, form.CurrentQuoteUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
40
Core/Handling/General/FileDialogHandler.cs
Normal file
40
Core/Handling/General/FileDialogHandler.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
sealed class FileDialogHandler : IDialogHandler{
|
||||
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
|
||||
CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask;
|
||||
|
||||
if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){
|
||||
string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter));
|
||||
|
||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||
AutoUpgradeEnabled = true,
|
||||
DereferenceLinks = true,
|
||||
Multiselect = dialogType == CefFileDialogMode.OpenMultiple,
|
||||
Title = "Open Files",
|
||||
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
|
||||
}){
|
||||
if (dialog.ShowDialog() == DialogResult.OK){
|
||||
callback.Continue(acceptFilters.FindIndex(filter => filter == Path.GetExtension(dialog.FileName)), dialog.FileNames.ToList());
|
||||
}
|
||||
else{
|
||||
callback.Cancel();
|
||||
}
|
||||
|
||||
callback.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
@@ -11,7 +12,7 @@ namespace TweetDuck.Core.Handling.General{
|
||||
case WindowOpenDisposition.NewForegroundTab:
|
||||
case WindowOpenDisposition.NewPopup:
|
||||
case WindowOpenDisposition.NewWindow:
|
||||
BrowserUtils.OpenExternalBrowser(targetUrl);
|
||||
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@@ -82,9 +82,10 @@ namespace TweetDuck.Core.Notification{
|
||||
private TweetNotification currentNotification;
|
||||
private int pauseCounter;
|
||||
|
||||
public string CurrentChirpId => currentNotification?.ChirpId;
|
||||
public string CurrentTweetUrl => currentNotification?.TweetUrl;
|
||||
public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
|
||||
|
||||
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
|
||||
public bool IsPaused => pauseCounter > 0;
|
||||
|
||||
public bool FreezeTimer { get; set; }
|
||||
@@ -144,7 +145,7 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||
if (e.IsBrowserInitialized){
|
||||
Initialized?.Invoke(this, new EventArgs());
|
||||
Initialized?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
int identifier = browser.GetBrowser().Identifier;
|
||||
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
|
||||
|
@@ -128,11 +128,16 @@ namespace TweetDuck.Core.Other{
|
||||
tab.Control.OnReady();
|
||||
}
|
||||
|
||||
panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything
|
||||
panelContents.PerformLayout();
|
||||
|
||||
panelContents.SuspendLayout();
|
||||
panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
|
||||
panelContents.Controls.Clear();
|
||||
panelContents.Controls.Add(tab.Control);
|
||||
panelContents.ResumeLayout(true);
|
||||
|
||||
panelContents.VerticalScroll.Enabled = true;
|
||||
panelContents.Focus();
|
||||
|
||||
currentTab = tab;
|
||||
|
@@ -173,7 +173,7 @@ namespace TweetDuck.Core.Other.Management{
|
||||
}
|
||||
|
||||
private void TriggerProcessExitEventUnsafe(){
|
||||
ProcessExited?.Invoke(this, new EventArgs());
|
||||
ProcessExited?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
tbDataFolder.TextChanged += control_Change;
|
||||
}
|
||||
|
||||
control_Change(this, new EventArgs());
|
||||
control_Change(this, EventArgs.Empty);
|
||||
|
||||
Text = Program.BrandName+" Arguments";
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
labelVolumeValue.Text = trackBarVolume.Value+"%";
|
||||
|
||||
tbCustomSound.Text = Config.NotificationSoundPath;
|
||||
tbCustomSound_TextChanged(tbCustomSound, new EventArgs());
|
||||
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
|
||||
|
||||
Disposed += (sender, args) => soundNotification.Dispose();
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
@@ -42,23 +43,46 @@ namespace TweetDuck.Core.Utils{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidUrl(string url){
|
||||
public static ChromiumWebBrowser AsControl(this IWebBrowser browserControl){
|
||||
return (ChromiumWebBrowser)browserControl;
|
||||
}
|
||||
|
||||
private const string TwitterTrackingUrl = "t.co";
|
||||
|
||||
public enum UrlCheckResult{
|
||||
Invalid, Tracking, Fine
|
||||
}
|
||||
|
||||
public static UrlCheckResult CheckUrl(string url){
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||
string scheme = uri.Scheme;
|
||||
return scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto;
|
||||
|
||||
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
|
||||
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return UrlCheckResult.Invalid;
|
||||
}
|
||||
|
||||
public static void OpenExternalBrowser(string url){
|
||||
if (string.IsNullOrWhiteSpace(url))return;
|
||||
|
||||
if (IsValidUrl(url)){
|
||||
OpenExternalBrowserUnsafe(url);
|
||||
}
|
||||
else{
|
||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||
switch(CheckUrl(url)){
|
||||
case UrlCheckResult.Fine:
|
||||
OpenExternalBrowserUnsafe(url);
|
||||
break;
|
||||
|
||||
case UrlCheckResult.Tracking:
|
||||
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
|
||||
OpenExternalBrowserUnsafe(url);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UrlCheckResult.Invalid:
|
||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ namespace TweetDuck.Core.Utils{
|
||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
||||
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
|
||||
|
||||
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
|
||||
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", RegexOptions.Compiled), false);
|
||||
public static Regex RegexAccount => RegexAccountLazy.Value;
|
||||
|
||||
public static readonly string[] DictionaryWords = {
|
||||
|
@@ -21,7 +21,7 @@ namespace TweetDuck{
|
||||
public const string BrandName = "TweetDuck";
|
||||
public const string Website = "https://tweetduck.chylex.com";
|
||||
|
||||
public const string VersionTag = "1.9";
|
||||
public const string VersionTag = "1.9.1";
|
||||
|
||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||
|
||||
|
@@ -334,17 +334,22 @@ enabled(){
|
||||
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
||||
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||
|
||||
let notificationScrollbarColor = null;
|
||||
|
||||
if (this.config.themeColorTweaks){
|
||||
switch(TD.settings.getTheme()){
|
||||
case "dark":
|
||||
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }");
|
||||
this.css.insert(".column-drag-handle { opacity: 0.5 }");
|
||||
this.css.insert(".column-drag-handle:hover { opacity: 1 }");
|
||||
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #666 }");
|
||||
notificationScrollbarColor = "666";
|
||||
break;
|
||||
|
||||
case "light":
|
||||
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #d2d6da }");
|
||||
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }");
|
||||
notificationScrollbarColor = "a5aeb5";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -460,6 +465,9 @@ enabled(){
|
||||
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
||||
.column-header .column-type-icon { bottom: 26px !important }
|
||||
.is-options-open .column-type-icon { bottom: 25px !important }
|
||||
|
||||
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
||||
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
||||
.tweet-footer { margin-top: 6px !important }`;
|
||||
|
||||
document.head.appendChild(this.icons);
|
||||
@@ -511,6 +519,10 @@ ${this.config.revertIcons ? `
|
||||
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
||||
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
|
||||
` : ``}
|
||||
|
||||
${notificationScrollbarColor ? `
|
||||
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} }
|
||||
` : ``}
|
||||
</style>`);
|
||||
};
|
||||
|
||||
|
@@ -356,10 +356,9 @@
|
||||
var prevMouseX = -1, prevMouseY = -1;
|
||||
var tooltipTimer, tooltipDisplayed;
|
||||
|
||||
$(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
|
||||
var me = $(this);
|
||||
|
||||
if (e.type === "mouseenter"){
|
||||
$(document.body).delegate("a[data-full-url]", {
|
||||
mouseenter: function(){
|
||||
let me = $(this);
|
||||
let text = me.text();
|
||||
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis
|
||||
|
||||
@@ -380,14 +379,13 @@
|
||||
tooltipDisplayed = true;
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
else if (e.type === "mouseleave"){
|
||||
if ($TDX.expandLinksOnHover){
|
||||
let prevText = me.attr("td-prev-text");
|
||||
|
||||
if (prevText){
|
||||
me.text(prevText);
|
||||
}
|
||||
},
|
||||
|
||||
mouseleave: function(){
|
||||
let me = $(this);
|
||||
|
||||
if (me[0].hasAttribute("td-prev-text")){
|
||||
me.text(me.attr("td-prev-text"));
|
||||
}
|
||||
|
||||
window.clearTimeout(tooltipTimer);
|
||||
@@ -396,10 +394,11 @@
|
||||
tooltipDisplayed = false;
|
||||
$TD.displayTooltip(null, false);
|
||||
}
|
||||
}
|
||||
else if (e.type === "mousemove"){
|
||||
},
|
||||
|
||||
mousemove: function(e){
|
||||
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
||||
$TD.displayTooltip(me.attr("data-full-url"), false);
|
||||
$TD.displayTooltip($(this).attr("data-full-url"), false);
|
||||
prevMouseX = e.clientX;
|
||||
prevMouseY = e.clientY;
|
||||
}
|
||||
@@ -408,7 +407,49 @@
|
||||
})();
|
||||
|
||||
//
|
||||
// Block: Allow bypassing of t.co and include media previews in context menus.
|
||||
// Block: Bypass t.co when clicking links and media.
|
||||
//
|
||||
$(document.body).delegate("a[data-full-url]", "click", function(e){
|
||||
$TD.openBrowser($(this).attr("data-full-url"));
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
|
||||
let prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
|
||||
|
||||
TD.services.TwitterUser.prototype.fromJSONObject = function(){
|
||||
let obj = prevFunc.apply(this, arguments);
|
||||
let e = arguments[0].entities;
|
||||
|
||||
if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
|
||||
obj.url = e.url.urls[0].expanded_url;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
||||
let prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
||||
|
||||
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
|
||||
let obj = prevFunc.apply(this, arguments);
|
||||
let e = arguments[0];
|
||||
|
||||
if (e.expanded_url){
|
||||
if (obj.url === obj.shortUrl){
|
||||
obj.shortUrl = e.expanded_url;
|
||||
}
|
||||
|
||||
obj.url = e.expanded_url;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Block: Include additional information in context menus.
|
||||
//
|
||||
$(document.body).delegate("a", "contextmenu", function(){
|
||||
let me = $(this)[0];
|
||||
@@ -450,29 +491,34 @@
|
||||
return !!highlightedColumnObj;
|
||||
};
|
||||
|
||||
var updateHighlightedTweet = function(ele, obj, link, embeddedLink, author, imageList){
|
||||
var updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
|
||||
highlightedTweetEle = ele;
|
||||
highlightedTweetObj = obj;
|
||||
|
||||
if (lastTweet !== link){
|
||||
$TD.setLastHighlightedTweet(link, embeddedLink, author, imageList);
|
||||
lastTweet = link;
|
||||
if (lastTweet !== tweetUrl){
|
||||
$TD.setLastHighlightedTweet(tweetUrl, quoteUrl, authors, imageList);
|
||||
lastTweet = tweetUrl;
|
||||
}
|
||||
};
|
||||
|
||||
app.delegate("section.js-column", "mouseenter mouseleave", function(e){
|
||||
if (e.type === "mouseenter"){
|
||||
var processMedia = function(media){
|
||||
return media.filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||
};
|
||||
|
||||
app.delegate("section.js-column", {
|
||||
mouseenter: function(){
|
||||
if (!highlightedColumnObj){
|
||||
updateHighlightedColumn($(this));
|
||||
}
|
||||
}
|
||||
else if (e.type === "mouseleave"){
|
||||
},
|
||||
|
||||
mouseleave: function(){
|
||||
updateHighlightedColumn(null);
|
||||
}
|
||||
});
|
||||
|
||||
app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
|
||||
if (e.type === "mouseenter"){
|
||||
app.delegate("article.js-stream-item", {
|
||||
mouseenter: function(){
|
||||
let me = $(this);
|
||||
return if !me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")));
|
||||
|
||||
@@ -480,19 +526,19 @@
|
||||
return if !tweet;
|
||||
|
||||
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
||||
let link = tweet.getChirpURL();
|
||||
let embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
||||
let username = tweet.getMainUser().screenName;
|
||||
let images = tweet.hasImage() ? tweet.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";") : "";
|
||||
// TODO maybe handle embedded images too?
|
||||
|
||||
updateHighlightedTweet(me, tweet, link || "", embedded || "", username, images);
|
||||
let tweetUrl = tweet.getChirpURL();
|
||||
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
||||
let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||
let imageList = tweet.quotedTweet ? processMedia(tweet.quotedTweet.getMedia()) : tweet.hasImage() ? processMedia(tweet.getMedia()) : "";
|
||||
|
||||
updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
|
||||
}
|
||||
else{
|
||||
updateHighlightedTweet(me, tweet, "", "", "", "");
|
||||
}
|
||||
}
|
||||
else if (e.type === "mouseleave"){
|
||||
},
|
||||
|
||||
mouseleave: function(){
|
||||
updateHighlightedTweet(null, null, "", "", "", "");
|
||||
}
|
||||
});
|
||||
@@ -704,9 +750,7 @@
|
||||
|
||||
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", onAccountClick);
|
||||
|
||||
if (!ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts")){
|
||||
return;
|
||||
}
|
||||
return if !ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts");
|
||||
|
||||
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
|
||||
if (!this.$node.attr("td-account-selector-hook")){
|
||||
@@ -764,8 +808,8 @@
|
||||
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
||||
addRule(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon in notifications
|
||||
|
||||
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, media previews
|
||||
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet { border-radius: 0 !important; }"); // square-ify dropdowns, quoted tweets, and account selectors
|
||||
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, dialogs, menus, media previews
|
||||
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select { border-radius: 0 !important; }"); // square-ify dropdowns, inputs, quoted tweets, and account selectors
|
||||
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
||||
|
||||
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
||||
|
@@ -14,14 +14,16 @@
|
||||
};
|
||||
|
||||
//
|
||||
// Block: Hook into links to bypass default open function.
|
||||
// Block: Hook into links to bypass default open function and t.co.
|
||||
//
|
||||
addEventListener(links, "click", function(e){
|
||||
$TD.openBrowser(e.currentTarget.getAttribute("href"));
|
||||
let ele = e.currentTarget;
|
||||
|
||||
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
|
||||
e.preventDefault();
|
||||
|
||||
if ($TDX.skipOnLinkClick){
|
||||
let parentClasses = e.currentTarget.parentNode.classList;
|
||||
let parentClasses = ele.parentNode.classList;
|
||||
|
||||
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
|
||||
$TD.loadNextNotification();
|
||||
@@ -33,7 +35,7 @@
|
||||
// Block: Allow bypassing of t.co in context menus.
|
||||
//
|
||||
addEventListener(links, "contextmenu", function(e){
|
||||
$TD.setLastRightClickedLink(e.currentTarget.getAttribute("data-full-url") || "");
|
||||
$TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
|
||||
});
|
||||
|
||||
//
|
||||
|
@@ -39,4 +39,25 @@
|
||||
};
|
||||
|
||||
setTimeout(injectCSS, 1);
|
||||
|
||||
//
|
||||
// Block: Make login page links external.
|
||||
//
|
||||
if (location.pathname === "/login"){
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
let openLinkExternally = function(e){
|
||||
let href = e.currentTarget.getAttribute("href");
|
||||
$TD.openBrowser(href[0] === '/' ? location.origin+href : href);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
let links = document.getElementsByTagName("A");
|
||||
|
||||
for(let index = 0; index < links.length; index++){
|
||||
links[index].addEventListener("click", openLinkExternally);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="packages\CefSharp.WinForms.57.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.57.0.0\build\CefSharp.WinForms.props')" />
|
||||
<Import Project="packages\CefSharp.Common.57.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.57.0.0\build\CefSharp.Common.props')" />
|
||||
@@ -97,6 +97,7 @@
|
||||
<Compile Include="Core\FormBrowser.Designer.cs">
|
||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Handling\General\FileDialogHandler.cs" />
|
||||
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
||||
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||
|
138
tests/Configuration/TestUserConfig.cs
Normal file
138
tests/Configuration/TestUserConfig.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core;
|
||||
|
||||
namespace UnitTests.Configuration{
|
||||
[TestClass]
|
||||
public class TestUserConfig : UnitTestIO{
|
||||
private static void WriteTestConfig(string file, bool withBackup){
|
||||
UserConfig cfg = UserConfig.Load(file);
|
||||
cfg.ZoomLevel = 123;
|
||||
cfg.Save();
|
||||
|
||||
if (withBackup){
|
||||
cfg.Save();
|
||||
}
|
||||
}
|
||||
|
||||
[Pure] // used to display a warning when not using the return value
|
||||
private static bool CheckTestConfig(string file){
|
||||
return UserConfig.Load(file).ZoomLevel == 123;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestMissing(){
|
||||
Assert.IsNotNull(UserConfig.Load("missing"));
|
||||
Assert.IsFalse(File.Exists("missing"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBasic(){
|
||||
Assert.IsFalse(CheckTestConfig("basic"));
|
||||
WriteTestConfig("basic", false);
|
||||
Assert.IsTrue(CheckTestConfig("basic"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBackupName(){
|
||||
Assert.AreEqual("name.bak", UserConfig.GetBackupFile("name"));
|
||||
Assert.AreEqual("name.cfg.bak", UserConfig.GetBackupFile("name.cfg"));
|
||||
Assert.AreEqual("name.bak.bak", UserConfig.GetBackupFile("name.bak"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBackupCreate(){
|
||||
WriteTestConfig("nobackup", false);
|
||||
Assert.IsTrue(File.Exists("nobackup"));
|
||||
Assert.IsFalse(File.Exists(UserConfig.GetBackupFile("nobackup")));
|
||||
|
||||
WriteTestConfig("withbackup", true);
|
||||
Assert.IsTrue(File.Exists("withbackup"));
|
||||
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("withbackup")));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBackupRestore(){
|
||||
WriteTestConfig("gone", true);
|
||||
Assert.IsTrue(File.Exists("gone"));
|
||||
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("gone")));
|
||||
File.Delete("gone");
|
||||
Assert.IsTrue(CheckTestConfig("gone"));
|
||||
|
||||
WriteTestConfig("corrupted", true);
|
||||
Assert.IsTrue(File.Exists("corrupted"));
|
||||
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("corrupted")));
|
||||
File.WriteAllText("corrupted", "oh no corrupt");
|
||||
Assert.IsTrue(CheckTestConfig("corrupted"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestReload(){
|
||||
UserConfig cfg = UserConfig.Load("reloaded");
|
||||
cfg.ZoomLevel = 123;
|
||||
cfg.Save();
|
||||
|
||||
cfg.ZoomLevel = 200;
|
||||
Assert.IsTrue(cfg.Reload());
|
||||
Assert.AreEqual(123, cfg.ZoomLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestReset(){
|
||||
UserConfig cfg = UserConfig.Load("reset");
|
||||
cfg.ZoomLevel = 123;
|
||||
cfg.Save();
|
||||
|
||||
File.Delete("reset");
|
||||
Assert.IsTrue(cfg.Reload());
|
||||
Assert.AreEqual(100, cfg.ZoomLevel);
|
||||
Assert.IsTrue(File.Exists("reset"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEventsNoTrigger(){
|
||||
void Fail(object sender, EventArgs args) => Assert.Fail();
|
||||
|
||||
UserConfig cfg = UserConfig.Load("events");
|
||||
cfg.MuteNotifications = true;
|
||||
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||
cfg.ZoomLevel = 99;
|
||||
|
||||
cfg.MuteToggled += Fail;
|
||||
cfg.TrayBehaviorChanged += Fail;
|
||||
cfg.ZoomLevelChanged += Fail;
|
||||
|
||||
cfg.MuteNotifications = true;
|
||||
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||
cfg.ZoomLevel = 99;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEventsTrigger(){
|
||||
int triggers = 0;
|
||||
void Trigger(object sender, EventArgs args) => ++triggers;
|
||||
|
||||
UserConfig cfg = UserConfig.Load("events");
|
||||
cfg.MuteNotifications = false;
|
||||
cfg.TrayBehavior = TrayIcon.Behavior.Disabled;
|
||||
cfg.ZoomLevel = 100;
|
||||
|
||||
cfg.MuteToggled += Trigger;
|
||||
cfg.TrayBehaviorChanged += Trigger;
|
||||
cfg.ZoomLevelChanged += Trigger;
|
||||
|
||||
cfg.MuteNotifications = true;
|
||||
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||
cfg.ZoomLevel = 99;
|
||||
|
||||
cfg.MuteNotifications = false;
|
||||
cfg.TrayBehavior = TrayIcon.Behavior.Disabled;
|
||||
cfg.ZoomLevel = 100;
|
||||
|
||||
Assert.AreEqual(6, triggers);
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,33 +6,36 @@ namespace UnitTests.Core{
|
||||
public class TestBrowserUtils{
|
||||
[TestMethod]
|
||||
public void TestIsValidUrl(){
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com")); // base
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://www.google.com")); // www.
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.co.uk")); // co.uk
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com")); // base
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://www.google.com")); // www.
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.co.uk")); // co.uk
|
||||
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("https://google.com")); // https
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("ftp://google.com")); // ftp
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("mailto:someone@google.com")); // mailto
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("https://google.com")); // https
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("ftp://google.com")); // ftp
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("mailto:someone@google.com")); // mailto
|
||||
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/")); // trailing slash
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?")); // trailing question mark
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x")); // parameters
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/#hash")); // parameters + hash
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/")); // trailing slash
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?")); // trailing question mark
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x")); // parameters
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/#hash")); // parameters + hash
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
||||
|
||||
foreach(string tld in new string[]{ "accountants", "blackfriday", "cancerresearch", "coffee", "cool", "foo", "travelersinsurance" }){
|
||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://test."+tld)); // long and unusual TLDs
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://test."+tld)); // long and unusual TLDs
|
||||
}
|
||||
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer")); // file
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer.exe")); // file
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("://explorer.exe")); // file-sorta
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("file://explorer.exe")); // file-proper
|
||||
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("")); // empty
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("lol")); // random
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("http://t.co/abc")); // tracking
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("https://t.co/abc")); // tracking
|
||||
|
||||
Assert.IsFalse(BrowserUtils.IsValidUrl("gopher://nobody.cares")); // lmao rekt
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer")); // file
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer.exe")); // file
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("://explorer.exe")); // file-sorta
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("file://explorer.exe")); // file-proper
|
||||
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("")); // empty
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("lol")); // random
|
||||
|
||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("gopher://nobody.cares")); // lmao rekt
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@@ -6,39 +6,39 @@ using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestCombinedFileStream{
|
||||
public class TestCombinedFileStream : UnitTestIO{
|
||||
[TestMethod]
|
||||
public void TestNoFiles(){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_empty"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("empty"))){
|
||||
cfs.Flush();
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_empty"));
|
||||
Assert.IsTrue(File.Exists("empty"));
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("empty"))){
|
||||
Assert.IsNull(cfs.ReadFile());
|
||||
}
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("empty"))){
|
||||
Assert.IsNull(cfs.SkipFile());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEmptyFiles(){
|
||||
TestUtils.WriteText("cfs_input_empty_1", string.Empty);
|
||||
TestUtils.WriteText("cfs_input_empty_2", string.Empty);
|
||||
File.WriteAllText("input_empty_1", string.Empty);
|
||||
File.WriteAllText("input_empty_2", string.Empty);
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_blank_files"))){
|
||||
cfs.WriteFile("id1", "cfs_input_empty_1");
|
||||
cfs.WriteFile("id2", "cfs_input_empty_2");
|
||||
cfs.WriteFile("id2_clone", "cfs_input_empty_2");
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("blank_files"))){
|
||||
cfs.WriteFile("id1", "input_empty_1");
|
||||
cfs.WriteFile("id2", "input_empty_2");
|
||||
cfs.WriteFile("id2_clone", "input_empty_2");
|
||||
cfs.Flush();
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_blank_files"));
|
||||
Assert.IsTrue(File.Exists("blank_files"));
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_blank_files"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("blank_files"))){
|
||||
CombinedFileStream.Entry entry1 = cfs.ReadFile();
|
||||
string entry2key = cfs.SkipFile();
|
||||
CombinedFileStream.Entry entry3 = cfs.ReadFile();
|
||||
@@ -56,31 +56,29 @@ namespace UnitTests.Data{
|
||||
Assert.AreEqual("id2_clone", entry3.Identifier);
|
||||
CollectionAssert.AreEqual(new string[0], entry3.KeyValue);
|
||||
|
||||
entry1.WriteToFile("cfs_blank_file_1");
|
||||
entry3.WriteToFile("cfs_blank_file_2");
|
||||
TestUtils.DeleteFileOnExit("cfs_blank_file_1");
|
||||
TestUtils.DeleteFileOnExit("cfs_blank_file_2");
|
||||
entry1.WriteToFile("blank_file_1");
|
||||
entry3.WriteToFile("blank_file_2");
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_blank_file_1"));
|
||||
Assert.IsTrue(File.Exists("cfs_blank_file_2"));
|
||||
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_1"));
|
||||
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_2"));
|
||||
Assert.IsTrue(File.Exists("blank_file_1"));
|
||||
Assert.IsTrue(File.Exists("blank_file_2"));
|
||||
Assert.AreEqual(string.Empty, File.ReadAllText("blank_file_1"));
|
||||
Assert.AreEqual(string.Empty, File.ReadAllText("blank_file_2"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestTextFilesAndComplexKeys(){
|
||||
TestUtils.WriteText("cfs_input_text_1", "Hello World!"+Environment.NewLine);
|
||||
File.WriteAllText("input_text_1", "Hello World!"+Environment.NewLine);
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_text_files"))){
|
||||
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
||||
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("text_files"))){
|
||||
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "input_text_1");
|
||||
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "input_text_1");
|
||||
cfs.Flush();
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_text_files"));
|
||||
Assert.IsTrue(File.Exists("text_files"));
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_text_files"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("text_files"))){
|
||||
CombinedFileStream.Entry entry = cfs.ReadFile();
|
||||
|
||||
Assert.AreEqual("key2", cfs.SkipFile());
|
||||
@@ -91,68 +89,65 @@ namespace UnitTests.Data{
|
||||
Assert.AreEqual("key1", entry.KeyName);
|
||||
CollectionAssert.AreEqual(new string[]{ "a", "bb", "ccc", "dddd" }, entry.KeyValue);
|
||||
|
||||
entry.WriteToFile("cfs_text_file_1");
|
||||
TestUtils.DeleteFileOnExit("cfs_text_file_1");
|
||||
entry.WriteToFile("text_file_1");
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_text_file_1"));
|
||||
Assert.AreEqual("Hello World!"+Environment.NewLine, TestUtils.ReadText("cfs_text_file_1"));
|
||||
Assert.IsTrue(File.Exists("text_file_1"));
|
||||
Assert.AreEqual("Hello World!"+Environment.NewLine, File.ReadAllText("text_file_1"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEntryWriteWithDirectory(){
|
||||
if (Directory.Exists("cfs_directory")){
|
||||
Directory.Delete("cfs_directory", true);
|
||||
if (Directory.Exists("directory")){
|
||||
Directory.Delete("directory", true);
|
||||
}
|
||||
|
||||
TestUtils.WriteText("cfs_input_dir_1", "test");
|
||||
File.WriteAllText("input_dir_1", "test");
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_dir_test"))){
|
||||
cfs.WriteFile("key1", "cfs_input_dir_1");
|
||||
cfs.WriteFile("key2", "cfs_input_dir_1");
|
||||
cfs.WriteFile("key3", "cfs_input_dir_1");
|
||||
cfs.WriteFile("key4", "cfs_input_dir_1");
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("dir_test"))){
|
||||
cfs.WriteFile("key1", "input_dir_1");
|
||||
cfs.WriteFile("key2", "input_dir_1");
|
||||
cfs.WriteFile("key3", "input_dir_1");
|
||||
cfs.WriteFile("key4", "input_dir_1");
|
||||
cfs.Flush();
|
||||
}
|
||||
|
||||
Assert.IsTrue(File.Exists("cfs_dir_test"));
|
||||
Assert.IsTrue(File.Exists("dir_test"));
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_dir_test"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("dir_test"))){
|
||||
try{
|
||||
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", false);
|
||||
cfs.ReadFile().WriteToFile("directory/dir_test_file", false);
|
||||
Assert.Fail("WriteToFile did not trigger an exception.");
|
||||
}catch(DirectoryNotFoundException){}
|
||||
|
||||
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", true);
|
||||
cfs.ReadFile().WriteToFile("cfs_dir_test_file", true);
|
||||
cfs.ReadFile().WriteToFile("cfs_dir_test_file.txt", true);
|
||||
TestUtils.DeleteFileOnExit("cfs_dir_test_file");
|
||||
TestUtils.DeleteFileOnExit("cfs_dir_test_file.txt");
|
||||
cfs.ReadFile().WriteToFile("directory/dir_test_file", true);
|
||||
cfs.ReadFile().WriteToFile("dir_test_file", true);
|
||||
cfs.ReadFile().WriteToFile("dir_test_file.txt", true);
|
||||
}
|
||||
|
||||
Assert.IsTrue(Directory.Exists("cfs_directory"));
|
||||
Assert.IsTrue(File.Exists("cfs_directory/cfs_dir_test_file"));
|
||||
Assert.IsTrue(File.Exists("cfs_dir_test_file"));
|
||||
Assert.IsTrue(File.Exists("cfs_dir_test_file.txt"));
|
||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_directory/cfs_dir_test_file"));
|
||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file"));
|
||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file.txt"));
|
||||
Assert.IsTrue(Directory.Exists("directory"));
|
||||
Assert.IsTrue(File.Exists("directory/dir_test_file"));
|
||||
Assert.IsTrue(File.Exists("dir_test_file"));
|
||||
Assert.IsTrue(File.Exists("dir_test_file.txt"));
|
||||
Assert.AreEqual("test", File.ReadAllText("directory/dir_test_file"));
|
||||
Assert.AreEqual("test", File.ReadAllText("dir_test_file"));
|
||||
Assert.AreEqual("test", File.ReadAllText("dir_test_file.txt"));
|
||||
|
||||
Directory.Delete("cfs_directory", true);
|
||||
Directory.Delete("directory", true);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestLongIdentifierSuccess(){
|
||||
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
|
||||
File.WriteAllText("long_identifier_fail_in", "test");
|
||||
|
||||
string identifier = string.Join("", Enumerable.Repeat("x", 255));
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_success"))){
|
||||
cfs.WriteFile(identifier, "cfs_long_identifier_fail_in");
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("long_identifier_success"))){
|
||||
cfs.WriteFile(identifier, "long_identifier_fail_in");
|
||||
cfs.Flush();
|
||||
}
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_long_identifier_success"))){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("long_identifier_success"))){
|
||||
Assert.AreEqual(identifier, cfs.ReadFile().Identifier);
|
||||
}
|
||||
}
|
||||
@@ -160,10 +155,10 @@ namespace UnitTests.Data{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void TestLongIdentifierFail(){
|
||||
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
|
||||
File.WriteAllText("long_identifier_fail_in", "test");
|
||||
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_fail"))){
|
||||
cfs.WriteFile(string.Join("", Enumerable.Repeat("x", 256)), "cfs_long_identifier_fail_in");
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("long_identifier_fail"))){
|
||||
cfs.WriteFile(string.Join("", Enumerable.Repeat("x", 256)), "long_identifier_fail_in");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestFileSerializer{
|
||||
public class TestFileSerializer : UnitTestIO{
|
||||
private enum TestEnum{
|
||||
A, B, C, D, E
|
||||
}
|
||||
@@ -30,13 +30,11 @@ namespace UnitTests.Data{
|
||||
TestEnum = TestEnum.D
|
||||
};
|
||||
|
||||
serializer.Write("serialized_basic", write);
|
||||
|
||||
Assert.IsTrue(File.Exists("serialized_basic"));
|
||||
TestUtils.DeleteFileOnExit("serialized_basic");
|
||||
serializer.Write("basic_wr", write);
|
||||
Assert.IsTrue(File.Exists("basic_wr"));
|
||||
|
||||
SerializationTestBasic read = new SerializationTestBasic();
|
||||
serializer.Read("serialized_basic", read);
|
||||
serializer.Read("basic_wr", read);
|
||||
|
||||
Assert.IsTrue(read.TestBool);
|
||||
Assert.AreEqual(-100, read.TestInt);
|
||||
|
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace UnitTests{
|
||||
public static class TestUtils{
|
||||
private static readonly HashSet<string> CreatedFiles = new HashSet<string>();
|
||||
|
||||
public static void WriteText(string file, string text){
|
||||
DeleteFileOnExit(file);
|
||||
File.WriteAllText(file, text, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static void WriteLines(string file, IEnumerable<string> lines){
|
||||
DeleteFileOnExit(file);
|
||||
File.WriteAllLines(file, lines, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static FileStream WriteFile(string file){
|
||||
DeleteFileOnExit(file);
|
||||
return new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
}
|
||||
|
||||
public static string ReadText(string file){
|
||||
try{
|
||||
return File.ReadAllText(file, Encoding.UTF8);
|
||||
}catch(Exception){
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ReadLines(string file){
|
||||
try{
|
||||
return File.ReadLines(file, Encoding.UTF8);
|
||||
}catch(Exception){
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public static FileStream ReadFile(string file){
|
||||
return new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None);
|
||||
}
|
||||
|
||||
public static void DeleteFileOnExit(string file){
|
||||
CreatedFiles.Add(file);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public static class Cleanup{
|
||||
[AssemblyCleanup]
|
||||
public static void DeleteFilesOnExit(){
|
||||
foreach(string file in CreatedFiles){
|
||||
try{
|
||||
File.Delete(file);
|
||||
}catch(Exception){
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
tests/UnitTestIO.cs
Normal file
32
tests/UnitTestIO.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace UnitTests{
|
||||
[TestClass]
|
||||
public class UnitTestIO{
|
||||
private static readonly HashSet<string> CreatedFolders = new HashSet<string>();
|
||||
|
||||
[TestInitialize]
|
||||
public void InitTest(){
|
||||
string folder = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, GetType().Name);
|
||||
CreatedFolders.Add(folder);
|
||||
Directory.CreateDirectory(folder);
|
||||
Directory.SetCurrentDirectory(folder);
|
||||
}
|
||||
|
||||
[AssemblyCleanup]
|
||||
public static void DeleteFilesOnExit(){
|
||||
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.SetupInformation.ApplicationBase);
|
||||
|
||||
foreach(string folder in CreatedFolders){
|
||||
try{
|
||||
Directory.Delete(folder, true);
|
||||
}catch(Exception){
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -47,6 +47,7 @@
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="Configuration\TestUserConfig.cs" />
|
||||
<Compile Include="Core\TestStringUtils.cs" />
|
||||
<Compile Include="Core\TestTwitterUtils.cs" />
|
||||
<Compile Include="Data\TestCombinedFileStream.cs" />
|
||||
@@ -56,7 +57,7 @@
|
||||
<Compile Include="Data\TestInjectedHTML.cs" />
|
||||
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestUtils.cs" />
|
||||
<Compile Include="UnitTestIO.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TweetDuck.csproj">
|
||||
|
Reference in New Issue
Block a user