Move EnsureSupportForCustomWebViewClients to Appium (#24167)
* Move EnsureSupportForCustomWebViewClients to Appium * - fix
This commit is contained in:
Родитель
f40b6c79b5
Коммит
e39d4b16f1
|
@ -0,0 +1,174 @@
|
|||
#if ANDROID
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Controls.Internals;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using Microsoft.Maui.Handlers;
|
||||
using AWebView = Android.Webkit.WebView;
|
||||
using IWebResourceRequest = Android.Webkit.IWebResourceRequest;
|
||||
using Microsoft.Maui.Platform;
|
||||
using WebResourceResponse = Android.Webkit.WebResourceResponse;
|
||||
|
||||
namespace Maui.Controls.Sample.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 16032, "Improve the customization of WebView on Android", PlatformAffected.Android)]
|
||||
public class Issue16032 : ContentPage
|
||||
{
|
||||
public Issue16032()
|
||||
{
|
||||
Content = new VerticalStackLayout()
|
||||
{
|
||||
new Issue16032WebView
|
||||
{
|
||||
Background = new SolidPaint(Colors.Red),
|
||||
WidthRequest = 300,
|
||||
HeightRequest = 300
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Issue16032WebView : WebView, IPropertyMapperView
|
||||
{
|
||||
PropertyMapper<Issue16032WebView, WebViewHandler> TestMapper =
|
||||
new PropertyMapper<Issue16032WebView, WebViewHandler>(WebViewHandler.Mapper);
|
||||
|
||||
public Issue16032WebView()
|
||||
{
|
||||
TestMapper.ModifyMapping(
|
||||
"WebViewClient",
|
||||
(handler, view, setter) =>
|
||||
{
|
||||
WebClient ??= new CustomWebClient((WebViewHandler)handler);
|
||||
handler.PlatformView.SetWebViewClient(WebClient);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected async override void OnHandlerChanged()
|
||||
{
|
||||
if (Handler is not WebViewHandler webViewHandler)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var platformWebView = webViewHandler.PlatformView;
|
||||
platformWebView.Settings.AllowFileAccess = true;
|
||||
|
||||
Source = new UrlWebViewSource { Url = "extracontent.html" };
|
||||
|
||||
var tcsLoaded = new TaskCompletionSource<bool>();
|
||||
var tcsNavigating = new TaskCompletionSource();
|
||||
var tcsRequested = new TaskCompletionSource();
|
||||
|
||||
// if the timeout happens, cancel everything
|
||||
var pageLoadTimeout = TimeSpan.FromSeconds(30);
|
||||
var ctsTimeout = new CancellationTokenSource(pageLoadTimeout);
|
||||
ctsTimeout.Token.Register(() =>
|
||||
{
|
||||
tcsRequested.TrySetException(new TimeoutException($"Failed to request the image"));
|
||||
tcsNavigating.TrySetException(new TimeoutException($"Failed to navigate to the loaded page"));
|
||||
tcsLoaded.TrySetException(new TimeoutException($"Failed to load HTML"));
|
||||
});
|
||||
|
||||
// attach some event handlers to track things
|
||||
var navigatingCount = 0;
|
||||
Navigating += ((sender, args) =>
|
||||
{
|
||||
navigatingCount++;
|
||||
|
||||
if (args.Url == "file:///android_asset/extracontent.html")
|
||||
tcsNavigating.TrySetResult();
|
||||
});
|
||||
var shouldRequestCount = 0;
|
||||
ShouldInterceptRequestDelegate = new((view, request) =>
|
||||
{
|
||||
shouldRequestCount++;
|
||||
|
||||
if (request.Url.ToString().StartsWith("https://raw.githubusercontent.com/dotnet/maui/4c096c1f17e9a23bf3961ba5778d3936039ad881/Assets/icon.png"))
|
||||
tcsRequested.TrySetResult();
|
||||
});
|
||||
|
||||
// set up a task to wait for the page to load
|
||||
Navigated += (sender, args) =>
|
||||
{
|
||||
// Set success when we have a successful nav result
|
||||
if (args.Result == WebNavigationResult.Success && args.Url == "file:///android_asset/extracontent.html")
|
||||
tcsLoaded.TrySetResult(args.Result == WebNavigationResult.Success);
|
||||
};
|
||||
|
||||
string failureMessage = String.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (WebClient is not CustomWebClient)
|
||||
{
|
||||
throw new Exception("CustomWebClient was not set");
|
||||
}
|
||||
|
||||
// wait for the navigation to complete
|
||||
await tcsNavigating.Task;
|
||||
if (navigatingCount < 1)
|
||||
{
|
||||
throw new Exception("Navigating event did not fire");
|
||||
}
|
||||
|
||||
if ((!await tcsLoaded.Task))
|
||||
{
|
||||
throw new Exception("HTML Source Failed to Load");
|
||||
}
|
||||
|
||||
// wait for the image to be requested
|
||||
await tcsRequested.Task;
|
||||
|
||||
if (shouldRequestCount != 1)
|
||||
{
|
||||
throw new Exception("only 1 request for the image to load");
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
failureMessage = ex.Message;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(failureMessage))
|
||||
{
|
||||
((VerticalStackLayout)Parent).Insert(0, new Label { Text = "All Expectations Have Been Met", AutomationId = "Success" });
|
||||
}
|
||||
else
|
||||
{
|
||||
((VerticalStackLayout)Parent).Insert(0, new Label { Text = failureMessage });
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMapper IPropertyMapperView.GetPropertyMapperOverrides() => TestMapper;
|
||||
|
||||
CustomWebClient WebClient { get; set; }
|
||||
|
||||
public Action<AWebView, IWebResourceRequest> ShouldInterceptRequestDelegate { get; set; }
|
||||
|
||||
class CustomWebClient : MauiWebViewClient
|
||||
{
|
||||
WebViewHandler _handler;
|
||||
|
||||
public CustomWebClient(WebViewHandler handler)
|
||||
: base(handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public override WebResourceResponse ShouldInterceptRequest(AWebView view, IWebResourceRequest request)
|
||||
{
|
||||
if (_handler.VirtualView is Issue16032WebView customWebView)
|
||||
customWebView.ShouldInterceptRequestDelegate(view, request);
|
||||
|
||||
return base.ShouldInterceptRequest(view, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
#if ANDROID
|
||||
using NUnit.Framework;
|
||||
using UITest.Appium;
|
||||
using UITest.Core;
|
||||
|
||||
namespace Microsoft.Maui.TestCases.Tests.Issues
|
||||
{
|
||||
public class Issue16032 : _IssuesUITest
|
||||
{
|
||||
public Issue16032(TestDevice device)
|
||||
: base(device)
|
||||
{ }
|
||||
|
||||
public override string Issue => "Improve the customization of WebView on Android";
|
||||
|
||||
[Test]
|
||||
[Category(UITestCategories.WebView)]
|
||||
public void EnsureSupportForCustomWebViewClients()
|
||||
{
|
||||
App.WaitForElement("Success");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -10,145 +10,10 @@ namespace Microsoft.Maui.DeviceTests
|
|||
{
|
||||
public partial class WebViewHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public Task EnsureSupportForCustomWebViewClients() =>
|
||||
InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
// create the cross-platform view
|
||||
var webView = new WebViewStub
|
||||
{
|
||||
Width = 300,
|
||||
Height = 200,
|
||||
Background = new SolidPaint(Colors.Red),
|
||||
};
|
||||
|
||||
// create the platform view
|
||||
await AttachAndRun<CustomWebViewHandler>(webView, async webViewHandler =>
|
||||
{
|
||||
var platformWebView = webViewHandler.PlatformView;
|
||||
|
||||
var tcsLoaded = new TaskCompletionSource<bool>();
|
||||
var tcsNavigating = new TaskCompletionSource();
|
||||
var tcsRequested = new TaskCompletionSource();
|
||||
|
||||
// if the timeout happens, cancel everything
|
||||
var pageLoadTimeout = TimeSpan.FromSeconds(30);
|
||||
var ctsTimeout = new CancellationTokenSource(pageLoadTimeout);
|
||||
ctsTimeout.Token.Register(() =>
|
||||
{
|
||||
tcsLoaded.TrySetException(new TimeoutException($"Failed to load HTML"));
|
||||
tcsNavigating.TrySetException(new TimeoutException($"Failed to navigate to the loaded page"));
|
||||
tcsRequested.TrySetException(new TimeoutException($"Failed to request the image"));
|
||||
});
|
||||
|
||||
// attach some event handlers to track things
|
||||
var navigatingCount = 0;
|
||||
webView.NavigatingDelegate = new((evnt, url) =>
|
||||
{
|
||||
navigatingCount++;
|
||||
|
||||
if (url == "file:///android_asset/extracontent.html")
|
||||
tcsNavigating.TrySetResult();
|
||||
|
||||
return false; // do not cancel the navigation
|
||||
});
|
||||
var shouldRequestCount = 0;
|
||||
webViewHandler.ShouldInterceptRequestDelegate = new((view, request) =>
|
||||
{
|
||||
shouldRequestCount++;
|
||||
|
||||
if (request.Url.ToString().StartsWith("https://raw.githubusercontent.com/dotnet/maui/4c096c1f17e9a23bf3961ba5778d3936039ad881/Assets/icon.png"))
|
||||
tcsRequested.TrySetResult();
|
||||
});
|
||||
|
||||
// set up a task to wait for the page to load
|
||||
webView.NavigatedDelegate = (evnt, url, result) =>
|
||||
{
|
||||
// Set success when we have a successful nav result
|
||||
if (result == WebNavigationResult.Success && url == "file:///android_asset/extracontent.html")
|
||||
tcsLoaded.TrySetResult(result == WebNavigationResult.Success);
|
||||
};
|
||||
|
||||
// load the page
|
||||
webView.Source = new UrlWebViewSourceStub { Url = "extracontent.html" };
|
||||
webViewHandler.UpdateValue(nameof(IWebView.Source));
|
||||
|
||||
// wait for the loaded event
|
||||
Assert.True(await tcsLoaded.Task, "HTML Source Failed to Load");
|
||||
|
||||
// make sure the mapper override fired at least once
|
||||
Assert.IsType<CustomWebClient>(webViewHandler.CustomWebClient);
|
||||
|
||||
// wait for the navigation to complete
|
||||
await tcsNavigating.Task;
|
||||
Assert.True(navigatingCount > 1); // at least 1 navigation, Android seems to do a few
|
||||
|
||||
// wait for the image to be requested
|
||||
await tcsRequested.Task;
|
||||
Assert.Equal(1, shouldRequestCount); // only 1 request for the image to load
|
||||
});
|
||||
});
|
||||
|
||||
AWebView GetNativeWebView(WebViewHandler webViewHandler) =>
|
||||
webViewHandler.PlatformView;
|
||||
|
||||
string GetNativeSource(WebViewHandler webViewHandler) =>
|
||||
GetNativeWebView(webViewHandler).Url;
|
||||
|
||||
class CustomWebViewHandler : WebViewHandler
|
||||
{
|
||||
// make a copy of the Core mappers because we don't want any Controls changes or to override us
|
||||
static IPropertyMapper<IWebView, IWebViewHandler> TestMapper =
|
||||
new PropertyMapper<IWebView, IWebViewHandler>(WebViewHandler.Mapper);
|
||||
static CommandMapper<IWebView, IWebViewHandler> TestCommandMapper =
|
||||
new(WebViewHandler.CommandMapper);
|
||||
|
||||
static CustomWebViewHandler()
|
||||
{
|
||||
// this is part of the test: testing the modify/replace the existing mapper
|
||||
TestMapper.ModifyMapping(
|
||||
nameof(WebViewClient),
|
||||
(handler, view, setter) =>
|
||||
{
|
||||
if (handler is not CustomWebViewHandler custom)
|
||||
throw new Exception("The CustomWebViewHandler.TestMapper is only meant to be used with the CustomWebViewHandler tests.");
|
||||
|
||||
if (custom.CustomWebClient is not null)
|
||||
throw new Exception("The [WebViewClient] mapper method is only supposed to be called once.");
|
||||
|
||||
custom.CustomWebClient = new CustomWebClient((CustomWebViewHandler)handler);
|
||||
|
||||
handler.PlatformView.SetWebViewClient(custom.CustomWebClient);
|
||||
});
|
||||
}
|
||||
|
||||
// make sure to use the Core mappers
|
||||
public CustomWebViewHandler()
|
||||
: base(TestMapper, TestCommandMapper)
|
||||
{
|
||||
}
|
||||
|
||||
public CustomWebClient CustomWebClient { get; private set; }
|
||||
|
||||
public Action<AWebView, IWebResourceRequest> ShouldInterceptRequestDelegate { get; set; }
|
||||
}
|
||||
|
||||
class CustomWebClient : MauiWebViewClient
|
||||
{
|
||||
CustomWebViewHandler _handler;
|
||||
|
||||
public CustomWebClient(CustomWebViewHandler handler)
|
||||
: base(handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public override WebResourceResponse ShouldInterceptRequest(AWebView view, IWebResourceRequest request)
|
||||
{
|
||||
_handler.ShouldInterceptRequestDelegate(view, request);
|
||||
|
||||
return base.ShouldInterceptRequest(view, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче