Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not important for this PR, but just like we do in portrait, in landscape we must eliminate the navigation bar.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue22606 : _IssuesUITest
{
public Issue22606(TestDevice device) : base(device) { }

public override string Issue => "Border does not expand on Content size changed";

[Test]
public void BorderBackgroundExpandsOnContentSizeChanged()
{
App.WaitForElement("SetHeightTo200");
App.Tap("SetHeightTo200");
VerifyScreenshot("Issue22606_SetHeightTo200");

App.Tap("SetHeightTo500");
VerifyScreenshot("Issue22606_SetHeightTo500");
}

#if ANDROID || IOS
[Test]
public void BorderBackgroundSizeUpdatesWhenRotatingScreen()
{
App.WaitForElement("SetHeightTo200");
App.Tap("SetHeightTo200");
App.SetOrientationLandscape();
VerifyScreenshot();
}
#endif
}
}
217 changes: 118 additions & 99 deletions src/Controls/tests/TestCases.Shared.Tests/UITest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ namespace Microsoft.Maui.TestCases.Tests
[TestFixture(TestDevice.Mac)]
#elif WINTEST
[TestFixture(TestDevice.Windows)]
#else
[TestFixture(TestDevice.iOS)]
[TestFixture(TestDevice.Mac)]
[TestFixture(TestDevice.Windows)]
[TestFixture(TestDevice.Android)]
#endif
public abstract class UITest : UITestBase
{
Expand Down Expand Up @@ -98,109 +93,133 @@ public override void Reset()

public void VerifyScreenshot(string? name = null)
{
string deviceName = GetTestConfig().GetProperty<string>("DeviceName") ?? string.Empty;
// Remove the XHarness suffix if present
deviceName = deviceName.Replace(" - created by XHarness", "", StringComparison.Ordinal);

/*
Determine the environmentName, used as the directory name for visual testing snaphots. Here are the rules/conventions:
- Names are lower case, no spaces.
- By default, the name matches the platform (android, ios, windows, or mac).
- Each platform has a default device (or set of devices) - if the snapshot matches the default no suffix is needed (e.g. just ios).
- If tests are run on secondary devices that produce different snapshots, the device name is used as suffix (e.g. ios-iphonex).
- If tests are run on secondary devices with multiple OS versions that produce different snapshots, both device name and os version are
used as a suffix (e.g. ios-iphonex-16_4). We don't have any cases of this today but may eventually. The device name comes first here,
before os version, because most visual testing differences come from different sceen size (a device thing), not OS version differences,
but both can happen.
*/
string environmentName = "";
switch (_testDevice)
// Retry the verification once in case the app is in a transient state
try
{
case TestDevice.Android:
if (deviceName == "Nexus 5X")
{
environmentName = "android";
}
else
{
Assert.Fail($"Android visual tests should be run on an Nexus 5X (API 30) emulator image, but the current device is '{deviceName}'. Follow the steps on the MAUI UI testing wiki.");
}
break;

case TestDevice.iOS:
if (deviceName == "iPhone Xs (iOS 17.2)")
{
environmentName = "ios";
}
else if (deviceName == "iPhone X (iOS 16.4)")
{
environmentName = "ios-iphonex";
}
else
{
Assert.Fail($"iOS visual tests should be run on iPhone Xs (iOS 17.2) or iPhone X (iOS 16.4) simulator images, but the current device is '{deviceName}'. Follow the steps on the MAUI UI testing wiki.");
}
break;

case TestDevice.Windows:
environmentName = "windows";
break;

case TestDevice.Mac:
// For now, ignore visual tests on Mac Catalyst since the Appium screenshot on Mac (unlike Windows)
// is of the entire screen, not just the app. Later when xharness relay support is in place to
// send a message to the MAUI app to get the screenshot, we can use that to just screenshot
// the app.
Assert.Ignore("MacCatalyst isn't supported yet for visual tests");
break;

default:
throw new NotImplementedException($"Unknown device type {_testDevice}");
Verify(name);
}

name ??= TestContext.CurrentContext.Test.MethodName ?? TestContext.CurrentContext.Test.Name;

// Currently Android is the OS with the ripple animations, but Windows may also have some animations
// that need to finish before taking a screenshot.
if (_testDevice == TestDevice.Android)
catch
{
Thread.Sleep(350);
Thread.Sleep(500);
Verify(name);
}

byte[] screenshotPngBytes = App.Screenshot() ?? throw new InvalidOperationException("Failed to get screenshot");

var actualImage = new ImageSnapshot(screenshotPngBytes, ImageSnapshotFormat.PNG);

// For Android and iOS, crop off the OS status bar at the top since it's not part of the
// app itself and contains the time, which always changes. For WinUI, crop off the title
// bar at the top as it varies slightly based on OS theme and is also not part of the app.
int cropFromTop = _testDevice switch
{
TestDevice.Android => 60,
TestDevice.iOS => environmentName == "ios-iphonex" ? 90 : 110,
TestDevice.Windows => 32,
_ => 0,
};

// For Android also crop the 3 button nav from the bottom, since it's not part of the
// app itself and the button color can vary (the buttons change clear briefly when tapped)
int cropFromBottom = _testDevice switch
void Verify(string? name)
{
TestDevice.Android => 125,
_ => 0,
};
string deviceName = GetTestConfig().GetProperty<string>("DeviceName") ?? string.Empty;
// Remove the XHarness suffix if present
deviceName = deviceName.Replace(" - created by XHarness", "", StringComparison.Ordinal);

/*
Determine the environmentName, used as the directory name for visual testing snaphots. Here are the rules/conventions:
- Names are lower case, no spaces.
- By default, the name matches the platform (android, ios, windows, or mac).
- Each platform has a default device (or set of devices) - if the snapshot matches the default no suffix is needed (e.g. just ios).
- If tests are run on secondary devices that produce different snapshots, the device name is used as suffix (e.g. ios-iphonex).
- If tests are run on secondary devices with multiple OS versions that produce different snapshots, both device name and os version are
used as a suffix (e.g. ios-iphonex-16_4). We don't have any cases of this today but may eventually. The device name comes first here,
before os version, because most visual testing differences come from different sceen size (a device thing), not OS version differences,
but both can happen.
*/
string environmentName = "";
switch (_testDevice)
{
case TestDevice.Android:
if (deviceName == "Nexus 5X")
{
environmentName = "android";
}
else
{
Assert.Fail($"Android visual tests should be run on an Nexus 5X (API 30) emulator image, but the current device is '{deviceName}'. Follow the steps on the MAUI UI testing wiki.");
}
break;

case TestDevice.iOS:
if (deviceName == "iPhone Xs (iOS 17.2)")
{
environmentName = "ios";
}
else if (deviceName == "iPhone X (iOS 16.4)")
{
environmentName = "ios-iphonex";
}
else
{
Assert.Fail($"iOS visual tests should be run on iPhone Xs (iOS 17.2) or iPhone X (iOS 16.4) simulator images, but the current device is '{deviceName}'. Follow the steps on the MAUI UI testing wiki.");
}
break;

case TestDevice.Windows:
environmentName = "windows";
break;

case TestDevice.Mac:
// For now, ignore visual tests on Mac Catalyst since the Appium screenshot on Mac (unlike Windows)
// is of the entire screen, not just the app. Later when xharness relay support is in place to
// send a message to the MAUI app to get the screenshot, we can use that to just screenshot
// the app.
Assert.Ignore("MacCatalyst isn't supported yet for visual tests");
break;

default:
throw new NotImplementedException($"Unknown device type {_testDevice}");
}

name ??= TestContext.CurrentContext.Test.MethodName ?? TestContext.CurrentContext.Test.Name;

// Currently Android is the OS with the ripple animations, but Windows may also have some animations
// that need to finish before taking a screenshot.
if (_testDevice == TestDevice.Android)
{
Thread.Sleep(350);
}

byte[] screenshotPngBytes = App.Screenshot() ?? throw new InvalidOperationException("Failed to get screenshot");

var actualImage = new ImageSnapshot(screenshotPngBytes, ImageSnapshotFormat.PNG);

// For Android and iOS, crop off the OS status bar at the top since it's not part of the
// app itself and contains the time, which always changes. For WinUI, crop off the title
// bar at the top as it varies slightly based on OS theme and is also not part of the app.
int cropFromTop = _testDevice switch
{
TestDevice.Android => 60,
TestDevice.iOS => environmentName == "ios-iphonex" ? 90 : 110,
TestDevice.Windows => 32,
_ => 0,
};

// For Android also crop the 3 button nav from the bottom, since it's not part of the
// app itself and the button color can vary (the buttons change clear briefly when tapped)
int cropFromBottom = _testDevice switch
{
TestDevice.Android => 125,
_ => 0,
};

if (cropFromTop > 0 || cropFromBottom > 0)
{
IImageEditor imageEditor = _imageEditorFactory.CreateImageEditor(actualImage);
(int width, int height) = imageEditor.GetSize();

imageEditor.Crop(0, cropFromTop, width, height - cropFromTop - cropFromBottom);

actualImage = imageEditor.GetUpdatedImage();
}

_visualRegressionTester.VerifyMatchesSnapshot(name!, actualImage, environmentName: environmentName, testContext: _visualTestContext);
}
}

if (cropFromTop > 0 || cropFromBottom > 0)
[SetUp]
public void TestSetup()
{
var device = App.GetTestDevice();
if(device == TestDevice.Android || device == TestDevice.iOS)
{
IImageEditor imageEditor = _imageEditorFactory.CreateImageEditor(actualImage);
(int width, int height) = imageEditor.GetSize();

imageEditor.Crop(0, cropFromTop, width, height - cropFromTop - cropFromBottom);

actualImage = imageEditor.GetUpdatedImage();
App.SetOrientationPortrait();
}

_visualRegressionTester.VerifyMatchesSnapshot(name!, actualImage, environmentName: environmentName, testContext: _visualTestContext);
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions src/Controls/tests/TestCases/Issues/Issue22606.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue22606"
Title="Issue 22606">
<VerticalStackLayout>
<Border Stroke="Red"
BackgroundColor="blue"
StrokeThickness="5"
Margin="20">
<Grid x:Name="content"
HeightRequest="200"
WidthRequest="300"/>
</Border>
<Button Text="Set Height to 200"
Clicked="OnSetHeightTo200Clicked"
AutomationId="SetHeightTo200"/>
<Button Text="Set Height to 500"
Clicked="OnSetHeightTo500Clicked"
AutomationId="SetHeightTo500"/>
</VerticalStackLayout>
</ContentPage>
26 changes: 26 additions & 0 deletions src/Controls/tests/TestCases/Issues/Issue22606.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 22606, "Border does not expand on Content size changed", PlatformAffected.All)]
public partial class Issue22606 : ContentPage
{
public Issue22606()
{
InitializeComponent();
}

void OnSetHeightTo200Clicked(object sender, EventArgs e)
{
content.HeightRequest = 200;
}

void OnSetHeightTo500Clicked(object sender, EventArgs e)
{
content.HeightRequest = 500;
}
}
}
25 changes: 7 additions & 18 deletions src/Core/src/Graphics/MauiDrawable.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public class MauiDrawable : PaintDrawable

bool _disposed;

ARect? _bounds;
int _width;
int _height;

Expand Down Expand Up @@ -347,31 +346,21 @@ public void SetBorderLineCap(LineCap lineCap)

public void InvalidateBorderBounds()
{
_bounds = null;

InvalidateSelf();
}

protected override void OnBoundsChange(ARect bounds)
{
if (_bounds != bounds)
{
_bounds = bounds;

if (_bounds != null)
{
var width = _bounds.Width();
var height = _bounds.Height();
var width = bounds.Width();
var height = bounds.Height();

if (_width == width && _height == height)
return;
if (_width == width && _height == height)
return;

_invalidatePath = true;
_invalidatePath = true;

_width = width;
_height = height;
}
}
_width = width;
_height = height;

base.OnBoundsChange(bounds);
}
Expand Down
14 changes: 0 additions & 14 deletions src/TestUtils/src/UITest.NUnit/UITestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,6 @@

namespace UITest.Appium.NUnit
{
//#if ANDROID
// [TestFixture(TestDevice.Android)]
//#elif IOSUITEST
// [TestFixture(TestDevice.iOS)]
//#elif MACUITEST
// [TestFixture(TestDevice.Mac)]
//#elif WINTEST
// [TestFixture(TestDevice.Windows)]
//#else
// [TestFixture(TestDevice.iOS)]
// [TestFixture(TestDevice.Mac)]
// [TestFixture(TestDevice.Windows)]
// [TestFixture(TestDevice.Android)]
//#endif
public abstract class UITestBase : UITestContextBase
{
public UITestBase(TestDevice testDevice)
Expand Down