From 73024b5bb4934bbca95225326239c86366b9586b Mon Sep 17 00:00:00 2001 From: Oliver Saal Date: Fri, 27 Jan 2017 13:48:22 -0800 Subject: [PATCH] Created UX test helper class and simplified the tests using the new UX pattern (#1855) * Created UX test helper class and simplified the tests using the new UX pattern CHANGES: 1. Created UXEvent class, wrapping NSCondition, and UIColor and WUXMSolidBrush comparison method 2. Added more controls to manipulate a UIButton via XAMLCatalog's viewcontroller 3. Applied new UX test pattern to CALayerAppearance tests Fixes #1764, #1757 --- .../FunctionalTests/FunctionalTests.vcxproj | 8 +- .../FunctionalTests.vcxproj.filters | 20 +- .../XAMLCatalog/XAMLCatalog-WinStore10.sln | 30 +-- .../Package.appxmanifest | 4 +- .../XAMLCatalog.vcxproj | 4 +- .../XAMLCatalog.vcxproj.filters | 22 +- .../XAMLCatalog.xcodeproj/project.pbxproj | 6 + .../XAMLCatalog/TestEnabledUITextField.h | 21 ++ .../XAMLCatalog/TestEnabledUITextField.mm | 28 +++ .../UIButtonWithControlsViewController.h | 15 +- .../UIButtonWithControlsViewController.m | 57 +++-- tests/functionaltests/MainViewController.mm | 2 +- .../CALayerAppearanceTests.mm | 194 ++++++------------ .../Tests/UIKitTests/UIButtonTests.mm | 72 ++----- tests/functionaltests/UXTestHelpers.h | 33 +++ tests/functionaltests/UXTestHelpers.mm | 41 ++++ 16 files changed, 311 insertions(+), 246 deletions(-) create mode 100644 samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.h create mode 100644 samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.mm diff --git a/build/Tests/FunctionalTests/FunctionalTests.vcxproj b/build/Tests/FunctionalTests/FunctionalTests.vcxproj index 982d0dce79..e0cf13aeee 100644 --- a/build/Tests/FunctionalTests/FunctionalTests.vcxproj +++ b/build/Tests/FunctionalTests/FunctionalTests.vcxproj @@ -246,6 +246,7 @@ + @@ -272,9 +273,6 @@ - - - @@ -293,6 +291,10 @@ true + + + + diff --git a/build/Tests/FunctionalTests/FunctionalTests.vcxproj.filters b/build/Tests/FunctionalTests/FunctionalTests.vcxproj.filters index fa0bbc9267..4e615c60a7 100644 --- a/build/Tests/FunctionalTests/FunctionalTests.vcxproj.filters +++ b/build/Tests/FunctionalTests/FunctionalTests.vcxproj.filters @@ -23,7 +23,7 @@ {34bd5c19-86ba-4661-bbb6-63295c1ca90f} - + {4c703c5d-0c21-4205-9e17-55fed2d45b67} @@ -43,13 +43,16 @@ - Tests\ViewControllers + Tests\ExternalUXResources + + + Tests\ExternalUXResources - Tests\ViewControllers + Tests\ExternalUXResources - Tests\ViewControllers + Tests\ExternalUXResources @@ -137,13 +140,16 @@ - Tests\ViewControllers + Tests\ExternalUXResources + + + Tests\ExternalUXResources - Tests\ViewControllers + Tests\ExternalUXResources - Tests\ViewControllers + Tests\ExternalUXResources diff --git a/samples/XAMLCatalog/XAMLCatalog-WinStore10.sln b/samples/XAMLCatalog/XAMLCatalog-WinStore10.sln index 85478656fa..dc792b7d98 100644 --- a/samples/XAMLCatalog/XAMLCatalog-WinStore10.sln +++ b/samples/XAMLCatalog/XAMLCatalog-WinStore10.sln @@ -2,9 +2,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.22823.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XAMLCatalog", "XAMLCatalog", "{FD57C970-6D9B-4AC7-BBBA-C589B2EA41BF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XAMLCatalog", "XAMLCatalog", "{AA7965C9-621F-4C5A-B4DE-2A49A00085BF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XAMLCatalog", "XAMLCatalog.vsimporter\XAMLCatalog-WinStore10\XAMLCatalog.vcxproj", "{D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XAMLCatalog", "XAMLCatalog.vsimporter\XAMLCatalog-WinStore10\XAMLCatalog.vcxproj", "{17FE0EC9-715B-4AB0-8491-5D31D3772A0A}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -16,23 +16,23 @@ Global Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|ARM.ActiveCfg = Debug|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|ARM.Build.0 = Debug|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|ARM.Deploy.0 = Debug|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|Win32.ActiveCfg = Debug|Win32 - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|Win32.Build.0 = Debug|Win32 - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Debug|Win32.Deploy.0 = Debug|Win32 - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|ARM.ActiveCfg = Release|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|ARM.Build.0 = Release|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|ARM.Deploy.0 = Release|ARM - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|Win32.ActiveCfg = Release|Win32 - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|Win32.Build.0 = Release|Win32 - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0}.Release|Win32.Deploy.0 = Release|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|ARM.ActiveCfg = Debug|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|ARM.Build.0 = Debug|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|ARM.Deploy.0 = Debug|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|Win32.ActiveCfg = Debug|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|Win32.Build.0 = Debug|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Debug|Win32.Deploy.0 = Debug|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|ARM.ActiveCfg = Release|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|ARM.Build.0 = Release|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|ARM.Deploy.0 = Release|ARM + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|Win32.ActiveCfg = Release|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|Win32.Build.0 = Release|Win32 + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A}.Release|Win32.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0} = {FD57C970-6D9B-4AC7-BBBA-C589B2EA41BF} + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A} = {AA7965C9-621F-4C5A-B4DE-2A49A00085BF} EndGlobalSection EndGlobal diff --git a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/Package.appxmanifest b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/Package.appxmanifest index 44132fbf8c..57ba5c477b 100644 --- a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/Package.appxmanifest +++ b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/Package.appxmanifest @@ -7,11 +7,11 @@ IgnorableNamespaces="uap mp"> - + XAMLCatalog diff --git a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj index cd35d323aa..216fc7e684 100644 --- a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj +++ b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj @@ -29,7 +29,7 @@ 10.0.14393.0 10.0.10586.0 IslandwoodProj - {D51F1FE7-DDDB-44C8-A77D-8901FFE9F5E0} + {17FE0EC9-715B-4AB0-8491-5D31D3772A0A} XAMLCatalog ..\..\..\.. @@ -195,6 +195,7 @@ + @@ -209,6 +210,7 @@ + diff --git a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj.filters b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj.filters index 34dad2e531..29dc644817 100644 --- a/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj.filters +++ b/samples/XAMLCatalog/XAMLCatalog.vsimporter/XAMLCatalog-WinStore10/XAMLCatalog.vcxproj.filters @@ -2,28 +2,28 @@ - {5b63b4d9-4cf3-4ee7-9c07-979a1b7a2b90} + {a89af828-b1b0-43eb-a93c-f0365abb8a2e} - {b90f1aee-77fa-432c-9e80-f335fb3ec883} + {e49647b2-ae29-46c1-a3e1-dacdf6b63691} - {F640AAC6-FC86-4B3E-B494-AEFDE936AA7B} + {CEF02F3B-93FF-4D69-A57E-1FC1ED26F52F} - {F46AF905-132A-46B5-A5F7-198B6EF7BF89} + {0D8051F2-4386-4572-85B1-84C24FBCDA8B} - {9C38E223-8855-4368-A64F-F9248851EAB7} + {AA46132E-2E52-4A04-8DC9-D4EC13902318} - {DD2EAD14-F632-40F4-B349-3CCBE2AC94A6} + {B8A238B4-288E-47B5-8CEE-83EEB7FD892F} - {40CE0495-F3FF-4BE6-8050-A0AE1D45915D} + {91E7FE91-D8C9-490E-97AE-F54669A95E47} - {B50ADFCA-EEB5-466C-8241-136AA5DEB14A} + {4840D6B9-B14D-4CD3-AB60-25E11864F178} @@ -72,6 +72,9 @@ XAMLCatalog + + XAMLCatalog + XAMLCatalog\UIKitControls @@ -114,6 +117,9 @@ XAMLCatalog + + XAMLCatalog + XAMLCatalog\UIKitControls diff --git a/samples/XAMLCatalog/XAMLCatalog.xcodeproj/project.pbxproj b/samples/XAMLCatalog/XAMLCatalog.xcodeproj/project.pbxproj index 3cfe69ef86..9073de5547 100644 --- a/samples/XAMLCatalog/XAMLCatalog.xcodeproj/project.pbxproj +++ b/samples/XAMLCatalog/XAMLCatalog.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 234767291E2EE058006D97CB /* UIViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 234767231E2EE058006D97CB /* UIViewViewController.m */; }; 2347672C1E2EE090006D97CB /* UIButtonWithControlsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2347672B1E2EE090006D97CB /* UIButtonWithControlsViewController.m */; }; 2347672F1E300CF0006D97CB /* UILabelViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2347672E1E300CF0006D97CB /* UILabelViewController.m */; }; + 239EBAEB1E39559C00C845D0 /* TestEnabledUITextField.mm in Sources */ = {isa = PBXBuildFile; fileRef = 239EBAEA1E39559C00C845D0 /* TestEnabledUITextField.mm */; }; 6EEB8A381CE4F4050021021E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EEB8A371CE4F4050021021E /* main.m */; }; 6EEB8A3B1CE4F4050021021E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EEB8A3A1CE4F4050021021E /* AppDelegate.m */; }; 6EEB8A3E1CE4F4050021021E /* ProgrammaticViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EEB8A3D1CE4F4050021021E /* ProgrammaticViewController.m */; }; @@ -47,6 +48,8 @@ 2347672B1E2EE090006D97CB /* UIButtonWithControlsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIButtonWithControlsViewController.m; path = UIKitControls/UIButtonWithControlsViewController.m; sourceTree = ""; }; 2347672D1E300CF0006D97CB /* UILabelViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UILabelViewController.h; path = UIKitControls/UILabelViewController.h; sourceTree = ""; }; 2347672E1E300CF0006D97CB /* UILabelViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UILabelViewController.m; path = UIKitControls/UILabelViewController.m; sourceTree = ""; }; + 239EBAE91E39559C00C845D0 /* TestEnabledUITextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestEnabledUITextField.h; sourceTree = ""; }; + 239EBAEA1E39559C00C845D0 /* TestEnabledUITextField.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestEnabledUITextField.mm; sourceTree = ""; }; 6EEB8A331CE4F4050021021E /* XAMLCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XAMLCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6EEB8A371CE4F4050021021E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 6EEB8A391CE4F4050021021E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -135,6 +138,8 @@ 6EEB8A3D1CE4F4050021021E /* ProgrammaticViewController.m */, 6EEB8A3F1CE4F4050021021E /* StoryBoardViewController.h */, 6EEB8A401CE4F4050021021E /* StoryBoardViewController.m */, + 239EBAE91E39559C00C845D0 /* TestEnabledUITextField.h */, + 239EBAEA1E39559C00C845D0 /* TestEnabledUITextField.mm */, 6EEB8A361CE4F4050021021E /* Supporting Files */, 2324634F1CE69E2C00B7AAE4 /* UIKitControls */, 7B67A2091DC366E1004BEF5D /* XamlControls */, @@ -245,6 +250,7 @@ 2347672C1E2EE090006D97CB /* UIButtonWithControlsViewController.m in Sources */, 234767281E2EE058006D97CB /* UITextFieldViewController.m in Sources */, 234767251E2EE058006D97CB /* UIActivityIndicatorViewController.m in Sources */, + 239EBAEB1E39559C00C845D0 /* TestEnabledUITextField.mm in Sources */, 6EEB8A411CE4F4050021021E /* StoryBoardViewController.m in Sources */, 234767261E2EE058006D97CB /* UIButtonViewController.m in Sources */, 6EEB8A3B1CE4F4050021021E /* AppDelegate.m in Sources */, diff --git a/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.h b/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.h new file mode 100644 index 0000000000..7bbec30885 --- /dev/null +++ b/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.h @@ -0,0 +1,21 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import "UIKit/UITextField.h" + +// Subclassed UITextField to support functional testing +@interface TestEnabledUITextField : UITextField +@end diff --git a/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.mm b/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.mm new file mode 100644 index 0000000000..73d653779b --- /dev/null +++ b/samples/XAMLCatalog/XAMLCatalog/TestEnabledUITextField.mm @@ -0,0 +1,28 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import "TestEnabledUITextField.h" + +#import + +@implementation TestEnabledUITextField +// NOTE: Programmatically setting the text via setText: does not trigger the text field delegates or events so we subclass +// UITextField and fire our editing event manually +- (void)setText:(NSString*)text { + [super setText:text]; + [self sendActionsForControlEvents:UIControlEventEditingChanged]; +} +@end diff --git a/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.h b/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.h index 36b02354c1..2942842963 100644 --- a/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.h +++ b/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.h @@ -15,17 +15,22 @@ //****************************************************************************** #import "MenuTableViewController.h" - -// Subclassed UITextField to support functional testing -@interface TestEnabledUITextField : UITextField -@end +#import "TestEnabledUITextField.h" @interface UIButtonWithControlsViewController : UIViewController @property (nonatomic, readonly) UIButton* button; -@property (nonatomic, readonly) TestEnabledUITextField* textTitleNormal; + @property (nonatomic, readonly) UISlider* sliderTitleColorNormal; +@property (nonatomic, readonly) UISwitch* switchEnabled; + +@property (nonatomic, readonly) TestEnabledUITextField* textTitleNormal; +@property (nonatomic, readonly) TestEnabledUITextField* textTitleHighlighted; +@property (nonatomic, readonly) TestEnabledUITextField* textTitleDisabled; + @property (nonatomic, readonly) UIColor* titleColorNormal; +@property (nonatomic, readonly) UIColor* titleColorHighlighted; +@property (nonatomic, readonly) UIColor* titleColorDisabled; @end diff --git a/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.m b/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.m index 7265a368a6..59b3ea08d0 100644 --- a/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.m +++ b/samples/XAMLCatalog/XAMLCatalog/UIKitControls/UIButtonWithControlsViewController.m @@ -17,15 +17,6 @@ #import "UIButtonWithControlsViewController.h" #import -@implementation TestEnabledUITextField -// NOTE: Programmatically setting the text via setText: does not trigger the text field delegates or events so we subclass -// UITextField and fire our editing event manually -- (void)setText:(NSString*)text { - [super setText:text]; - [self sendActionsForControlEvents:UIControlEventEditingChanged]; -} -@end - @implementation UIButtonWithControlsViewController { MenuTableViewController* _menuTVC; } @@ -48,6 +39,11 @@ - (void)viewDidLoad { [self.view addSubview:_button]; [self.view addSubview:_menuTVC.view]; + _switchEnabled = [[UISwitch alloc] init]; + _switchEnabled.on = YES; + [_switchEnabled addTarget:self action:@selector(onEnabled) forControlEvents:UIControlEventValueChanged]; + [_menuTVC addMenuItemView:_switchEnabled andTitle:@"Enabled"]; + // UITextField that changes the text on UIButton.titleLabel for normal state _textTitleNormal = [[TestEnabledUITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 40.0f)]; _textTitleNormal.textColor = [UIColor blackColor]; @@ -60,6 +56,30 @@ - (void)viewDidLoad { [_textTitleNormal addTarget:self action:@selector(onTextChanged:) forControlEvents:UIControlEventEditingChanged]; [_menuTVC addMenuItemView:_textTitleNormal andTitle:@"Text - normal state"]; + // UITextField that changes the text on UIButton.titleLabel for highlighted state + _textTitleHighlighted = [[TestEnabledUITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 40.0f)]; + _textTitleHighlighted.textColor = [UIColor blackColor]; + _textTitleHighlighted.backgroundColor = [UIColor lightGrayColor]; + _textTitleHighlighted.textAlignment = UITextAlignmentCenter; + _textTitleHighlighted.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; + _textTitleHighlighted.borderStyle = UITextBorderStyleLine; + _textTitleHighlighted.font = [UIFont systemFontOfSize:15.0f]; + _textTitleHighlighted.adjustsFontSizeToFitWidth = YES; + [_textTitleHighlighted addTarget:self action:@selector(onTextChangedHighlighted:) forControlEvents:UIControlEventEditingChanged]; + [_menuTVC addMenuItemView:_textTitleHighlighted andTitle:@"Text - highlighted state"]; + + // UITextField that changes the text on UIButton.titleLabel for disabled state + _textTitleDisabled = [[TestEnabledUITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 40.0f)]; + _textTitleDisabled.textColor = [UIColor blackColor]; + _textTitleDisabled.backgroundColor = [UIColor lightGrayColor]; + _textTitleDisabled.textAlignment = UITextAlignmentCenter; + _textTitleDisabled.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; + _textTitleDisabled.borderStyle = UITextBorderStyleLine; + _textTitleDisabled.font = [UIFont systemFontOfSize:15.0f]; + _textTitleDisabled.adjustsFontSizeToFitWidth = YES; + [_textTitleDisabled addTarget:self action:@selector(onTextChangedDisabled:) forControlEvents:UIControlEventEditingChanged]; + [_menuTVC addMenuItemView:_textTitleDisabled andTitle:@"Text - disabled state"]; + // UISlider that changes the text color on UIButton titleLabel for normal state _sliderTitleColorNormal = [[UISlider alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 40.0f)]; _sliderTitleColorNormal.minimumValue = 0.0f; @@ -67,18 +87,17 @@ - (void)viewDidLoad { _sliderTitleColorNormal.continuous = YES; _sliderTitleColorNormal.value = 0.0f; [_sliderTitleColorNormal addTarget:self action:@selector(titleColorNormalValueChanged) forControlEvents:UIControlEventValueChanged]; - [_menuTVC addMenuItemView:_sliderTitleColorNormal andTitle:@"Background color - normal state"]; + [_menuTVC addMenuItemView:_sliderTitleColorNormal andTitle:@"Title color - normal state"]; } // Initial configuration parameters for the UIButton - (void)setupButton { _button = [[UIButton alloc] initWithFrame:self.view.bounds]; - [_button setTitle:@"Button Normal" forState:UIControlStateNormal]; - [_button setTitle:@"Button Highlighted" forState:UIControlStateHighlighted]; - [_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - [_button setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted]; +} - _button.backgroundColor = [UIColor lightGrayColor]; +// Toggle if the button is enabled or disabled +- (void)onEnabled { + _button.enabled = _switchEnabled.on; } // Delegate to set the UIButton.titleLabel text @@ -86,6 +105,14 @@ - (void)onTextChanged:(UITextField*)textField { [_button setTitle:textField.text forState:UIControlStateNormal]; } +- (void)onTextChangedHighlighted:(UITextField*)textField { + [_button setTitle:textField.text forState:UIControlStateHighlighted]; +} + +- (void)onTextChangedDisabled:(UITextField*)textField { + [_button setTitle:textField.text forState:UIControlStateDisabled]; +} + // Delegate to dismiss the keyboard - (BOOL)textFieldShouldReturn:(UITextField*)textField { [textField resignFirstResponder]; diff --git a/tests/functionaltests/MainViewController.mm b/tests/functionaltests/MainViewController.mm index a639fb4230..ce757d18a1 100644 --- a/tests/functionaltests/MainViewController.mm +++ b/tests/functionaltests/MainViewController.mm @@ -29,7 +29,7 @@ @implementation MainViewController - (void)viewDidLoad { [super viewDidLoad]; - self.view.backgroundColor = [UIColor purpleColor]; + self.view.backgroundColor = [UIColor lightGrayColor]; } - (void)didReceiveMemoryWarning { diff --git a/tests/functionaltests/Tests/CoreAnimationTests/CALayerAppearanceTests.mm b/tests/functionaltests/Tests/CoreAnimationTests/CALayerAppearanceTests.mm index 68f7839b43..42fffaea6f 100644 --- a/tests/functionaltests/Tests/CoreAnimationTests/CALayerAppearanceTests.mm +++ b/tests/functionaltests/Tests/CoreAnimationTests/CALayerAppearanceTests.mm @@ -14,53 +14,29 @@ // //****************************************************************************** #import -#import +#import "FunctionalTestHelpers.h" +#import "UXTestHelpers.h" -#import -#import - -#include -#import -#import -#import -#import -#import -#import -#import -#import -#include - -#import "ObjCXamlControls.h" - -#import "UWP/WindowsUIXamlControls.h" #import "CALayerInternal.h" -// TODO: Consolidate this into a common place so that all tests can use it -static const NSTimeInterval c_testTimeoutInSec = 5; +using namespace UXTestAPI; @interface CALayerViewController : UIViewController -@property CALayer* layer; -@property UIView* viewForLayer; +@property (nonatomic, readonly) CALayer* layer; +@property (nonatomic, readonly) UIView* viewForLayer; @end @implementation CALayerViewController -- (instancetype)init { - if (self = [super init]) { - self->_layer = [CALayer layer]; - } - - return self; -} - - (void)viewDidLoad { [super viewDidLoad]; - self->_viewForLayer = self.view; - self->_viewForLayer.frame = CGRectMake(0, 100, 200, 200); - self->_viewForLayer.backgroundColor = [UIColor whiteColor]; + _viewForLayer = self.view; + _viewForLayer.frame = CGRectMake(0.0f, 00.f, 200.0f, 200.0f); + _viewForLayer.backgroundColor = [UIColor whiteColor]; - self->_layer.frame = self->_viewForLayer.bounds; - [self->_viewForLayer.layer addSublayer:self->_layer]; + _layer = [CALayer layer]; + _layer.frame = self->_viewForLayer.bounds; + [_viewForLayer.layer addSublayer:_layer]; } @end @@ -68,117 +44,67 @@ - (void)viewDidLoad { // Validate that the CALayer opacity property change invokes a property change in the backing XAML element // TEST(CALayerAppearance, OpacityChanged) { - __block CALayerViewController* caLayerVC; - __block BOOL signaled = NO; - __block NSCondition* condition = [[[NSCondition alloc] init] autorelease]; - __block WXUIElement* backingElement = nil; - - dispatch_sync(dispatch_get_main_queue(), ^{ - LOG_INFO("Creating CALayerViewController on the UI thread explicitly"); - caLayerVC = [[CALayerViewController alloc] init]; - - // TODO: Remove this line once we hook up to the root view controller which will trigger the view method - [caLayerVC view]; - - // We have to artificially ref the element since the block needs to keep it around - backingElement = [caLayerVC.layer _xamlElement]; - [backingElement retain]; - - // Register callback and wait for the property changed event to trigger - int64_t callbackToken = [backingElement - registerPropertyChangedCallback:[WXUIElement opacityProperty] - callback:^(WXDependencyObject* sender, WXDependencyProperty* dp) { - LOG_INFO("Backing XAML element opacity: %f", backingElement.opacity); - LOG_INFO("CALayer.opacity: %f", caLayerVC.layer.opacity); - - // Verification - EXPECT_EQ_MSG(backingElement.opacity, caLayerVC.layer.opacity, "Failed to match opacity"); - EXPECT_EQ_MSG(backingElement.opacity, 0.5f, "Failed to match opacity with expected value"); - - // Unregister the callback - [backingElement unregisterPropertyChangedCallback:[WXUIElement opacityProperty] token:callbackToken]; - - [condition lock]; - signaled = YES; - [condition signal]; - [condition unlock]; - }]; + __block auto uxEvent = UXEvent::CreateManual(); + __block auto xamlSubscriber = std::make_shared(); + + float expectedOpacity = 0.5f; + + CALayerViewController* caLayerVC = [[CALayerViewController alloc] init]; + UXTestAPI::ViewControllerPresenter testHelper(caLayerVC); + + dispatch_async(dispatch_get_main_queue(), ^{ + WXUIElement* xamlElement = [caLayerVC.layer _xamlElement]; + + // Register RAII event subscription handler + xamlSubscriber->Set(xamlElement, [WXUIElement opacityProperty], ^(WXDependencyObject* sender, WXDependencyProperty* dp) { + // Validation + EXPECT_EQ_MSG(xamlElement.opacity, caLayerVC.layer.opacity, "Failed to match opacity"); + EXPECT_EQ_MSG(xamlElement.opacity, expectedOpacity, "Failed to match opacity with expected value"); + + // Manually unregister the event so we avoid further opacity property changed events + xamlSubscriber->Reset(); + + uxEvent->Set(); + }); // Action - caLayerVC.layer.opacity = 0.5f; + caLayerVC.layer.opacity = expectedOpacity; }); - [condition lock]; - ASSERT_TRUE_MSG(signaled || [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:c_testTimeoutInSec]], - "FAILED: Waiting for property changed event timed out!"); - [condition unlock]; - - // Don't leak - [backingElement release]; - [caLayerVC release]; + ASSERT_TRUE_MSG(uxEvent->Wait(c_testTimeoutInSec), "FAILED: Waiting for property changed event timed out!"); } // // Validate that the CALayer background property change invokes a property change in the backing XAML element // TEST(CALayerAppearance, BackgroundColorChanged) { - __block CALayerViewController* caLayerVC; - __block BOOL signaled = NO; - __block NSCondition* condition = [[[NSCondition alloc] init] autorelease]; - __block WXUIElement* backingElement = nil; - - dispatch_sync(dispatch_get_main_queue(), ^{ - LOG_INFO("Creating CALayerViewController on the UI thread explicitly"); - caLayerVC = [[CALayerViewController alloc] init]; - [caLayerVC view]; - - // We have to artificially ref the element since the block needs to keep it around - backingElement = [caLayerVC.layer _xamlElement]; - [backingElement retain]; - - // Register callback and wait for the property changed event to trigger - int64_t callbackToken = [backingElement - registerPropertyChangedCallback:[WXCPanel backgroundProperty] - callback:^(WXDependencyObject* sender, WXDependencyProperty* dp) { - WUXMSolidColorBrush* solidBrush = rt_dynamic_cast([WUXMSolidColorBrush class], [sender getValue:dp]); - ASSERT_TRUE(solidBrush); - - LOG_INFO("Backing XAML element backgroundColor (rgba): %d,%d,%d,%d", - [solidBrush.color r], - [solidBrush.color g], - [solidBrush.color b], - [solidBrush.color a]); - - CGFloat red, green, blue, alpha; - [caLayerVC.layer.backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha]; - LOG_INFO("CALayer.backgroundColor (rgba): %.2f,%.2f,%.2f,%.2f", red, green, blue, alpha); - - // Validate that the change is reflected on the backing XAML control - EXPECT_EQ_MSG(solidBrush.color.r, (int)(red * 255), @"Failed to match red component"); - EXPECT_EQ_MSG(solidBrush.color.g, (int)(green * 255), @"Failed to match green component"); - EXPECT_EQ_MSG(solidBrush.color.b, (int)(blue * 255), @"Failed to match blue component"); - EXPECT_EQ_MSG(solidBrush.color.a, (int)(alpha * 255), @"Failed to match alpha component"); - - // Unregister the callback - [backingElement unregisterPropertyChangedCallback:[WXCPanel backgroundProperty] token:callbackToken]; - - [condition lock]; - signaled = YES; - [condition signal]; - [condition unlock]; - // - }]; + __block auto uxEvent = UXEvent::CreateManual(); + __block auto xamlSubscriber = std::make_shared(); + + UIColor* expectedColor = [UIColor redColor]; + + CALayerViewController* caLayerVC = [[CALayerViewController alloc] init]; + UXTestAPI::ViewControllerPresenter testHelper(caLayerVC); + + dispatch_async(dispatch_get_main_queue(), ^{ + WXUIElement* xamlElement = [caLayerVC.layer _xamlElement]; + + // Register RAII event subscription handler + xamlSubscriber->Set(xamlElement, [WXCPanel backgroundProperty], ^(WXDependencyObject* sender, WXDependencyProperty* dp) { + WUXMSolidColorBrush* solidBrush = rt_dynamic_cast([WUXMSolidColorBrush class], [sender getValue:dp]); + ASSERT_OBJCNE(solidBrush, nil); + + // Validation + UIColor* colorFromCGColor = [UIColor colorWithCGColor:caLayerVC.layer.backgroundColor]; + EXPECT_TRUE_MSG(UXTestAPI::IsRGBAEqual(solidBrush, colorFromCGColor), @"Failed to match XAML- and CALayer background color"); + EXPECT_OBJCEQ_MSG(colorFromCGColor, expectedColor, @"Failed to match expected color"); + + uxEvent->Set(); + }); // Action - caLayerVC.layer.backgroundColor = [UIColor redColor].CGColor; + caLayerVC.layer.backgroundColor = expectedColor.CGColor; }); - [condition lock]; - ASSERT_TRUE_MSG(signaled || [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:c_testTimeoutInSec]], - "FAILED: Waiting for property changed event timed out!"); - [condition unlock]; - - // Don't leak - [backingElement release]; - [caLayerVC release]; + ASSERT_TRUE_MSG(uxEvent->Wait(c_testTimeoutInSec), "FAILED: Waiting for property changed event timed out!"); } diff --git a/tests/functionaltests/Tests/UIKitTests/UIButtonTests.mm b/tests/functionaltests/Tests/UIKitTests/UIButtonTests.mm index 62f14339b7..78094cbb91 100644 --- a/tests/functionaltests/Tests/UIKitTests/UIButtonTests.mm +++ b/tests/functionaltests/Tests/UIKitTests/UIButtonTests.mm @@ -21,6 +21,8 @@ #import "UIKitControls/UIButtonViewController.h" #import "UIKitControls/UIButtonWithControlsViewController.h" +using namespace UXTestAPI; + TEST(UIButton, CreateXamlElement) { // TODO: Switch to UIKit.Xaml projections when they're available. Microsoft::WRL::ComPtr xamlElement(XamlCreateButton()); @@ -30,16 +32,15 @@ TEST(UIButton, GetXamlElement) { UIView* view = [[[UIButton alloc] init] autorelease]; WXFrameworkElement* backingElement = [view xamlElement]; - ASSERT_TRUE(backingElement); + ASSERT_OBJCNE(backingElement, nil); // TODO: Fix up when UIButton moves fully to XAML ASSERT_TRUE([backingElement isKindOfClass:[WXFrameworkElement class]]); } TEST(UIButton, TitleColorChanged) { - __block BOOL signaled = NO; - __block NSCondition* condition = [[NSCondition alloc] init]; - __block auto xamlSubscriber = std::make_shared(); + __block auto uxEvent = UXEvent::CreateManual(); + __block auto xamlSubscriber = std::make_shared(); UIButtonWithControlsViewController* buttonVC = [[UIButtonWithControlsViewController alloc] init]; UXTestAPI::ViewControllerPresenter testHelper(buttonVC); @@ -47,61 +48,33 @@ dispatch_async(dispatch_get_main_queue(), ^{ // Extract UIButton.titleLabel control to verify its visual state WXFrameworkElement* titleElement = [buttonVC.button.titleLabel xamlElement]; - ASSERT_TRUE(titleElement); + ASSERT_OBJCNE(titleElement, nil); // Register RAII event subscription handler xamlSubscriber->Set(titleElement, [WXCTextBlock foregroundProperty], ^(WXDependencyObject* sender, WXDependencyProperty* dp) { // Extract the foreground color from the XAML object WUXMSolidColorBrush* solidBrush = rt_dynamic_cast([WUXMSolidColorBrush class], [sender getValue:dp]); - LOG_INFO("XAML element color (rgba): %d,%d,%d,%d", - [solidBrush.color r], - [solidBrush.color g], - [solidBrush.color b], - [solidBrush.color a]); - // Extract the title color for the normal state + // Validation UIColor* titleColorNormal = [buttonVC.button titleColorForState:UIControlStateNormal]; + EXPECT_TRUE_MSG(UXTestAPI::IsRGBAEqual(solidBrush, titleColorNormal), @"Failed to match XAML- and UIButton title color"); + EXPECT_OBJCEQ_MSG(titleColorNormal, [buttonVC titleColorNormal], @"Failed to match expected color"); - CGFloat red, green, blue, alpha; - [titleColorNormal getRed:&red green:&green blue:&blue alpha:&alpha]; - LOG_INFO("UIButton.titleColorForState:normal (rgba): %.2f,%.2f,%.2f,%.2f", red, green, blue, alpha); - - // Validate that the change is reflected on the backing XAML control - EXPECT_EQ_MSG(solidBrush.color.r, (int)(red * 255), @"Failed to match red component"); - EXPECT_EQ_MSG(solidBrush.color.g, (int)(green * 255), @"Failed to match green component"); - EXPECT_EQ_MSG(solidBrush.color.b, (int)(blue * 255), @"Failed to match blue component"); - EXPECT_EQ_MSG(solidBrush.color.a, (int)(alpha * 255), @"Failed to match alpha component"); - - // Ensure that the XAML color values match the viewcontroller property - CGFloat VCred, VCgreen, VCblue, VCalpha; - [[buttonVC titleColorNormal] getRed:&VCred green:&VCgreen blue:&VCblue alpha:&VCalpha]; - EXPECT_EQ_MSG(solidBrush.color.r, (int)(red * 255), @"Failed to match red component on the VC"); - EXPECT_EQ_MSG(solidBrush.color.g, (int)(green * 255), @"Failed to match green component on the VC"); - EXPECT_EQ_MSG(solidBrush.color.b, (int)(blue * 255), @"Failed to match blue component on the VC"); - EXPECT_EQ_MSG(solidBrush.color.a, (int)(alpha * 255), @"Failed to match alpha component on the VC"); - - [condition lock]; - signaled = YES; - [condition signal]; - [condition unlock]; + uxEvent->Set(); }); // Action - validate this action takes effect on the control [buttonVC sliderTitleColorNormal].value = 150.0f; }); - [condition lock]; - ASSERT_TRUE_MSG(signaled || [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:c_testTimeoutInSec]], - "FAILED: Waiting for property changed event timed out!"); - [condition unlock]; + ASSERT_TRUE_MSG(uxEvent->Wait(c_testTimeoutInSec), "FAILED: Waiting for property changed event timed out!"); } TEST(UIButton, TextChanged) { - __block BOOL signaled = NO; - __block NSCondition* condition = [[NSCondition alloc] init]; - __block auto xamlSubscriber = std::make_shared(); + __block auto uxEvent = UXEvent::CreateManual(); + __block auto xamlSubscriber = std::make_shared(); - __block NSString* expectedString = @"Functional testing"; + NSString* expectedString = @"Functional testing"; UIButtonWithControlsViewController* buttonVC = [[UIButtonWithControlsViewController alloc] init]; UXTestAPI::ViewControllerPresenter testHelper(buttonVC); @@ -113,30 +86,19 @@ // Register RAII event subscription handler xamlSubscriber->Set(titleElement, [WXCTextBlock textProperty], ^(WXDependencyObject* sender, WXDependencyProperty* dp) { - // Extract the text from the XAML object NSString* text = UXTestAPI::NSStringFromPropertyValue([sender getValue:dp]); - LOG_INFO("TextBlock text: %@", text); - - // Extract the text for the normal state NSString* textNormal = [buttonVC.button titleForState:UIControlStateNormal]; - LOG_INFO("UIButton title - normal: %@", textNormal); - // Validate that the change is reflected on the backing XAML control + // Validation EXPECT_OBJCEQ_MSG(text, textNormal, @"Failed to match strings in XAML and UIButton"); EXPECT_OBJCEQ_MSG(text, expectedString, @"Failed to match expected string"); - [condition lock]; - signaled = YES; - [condition signal]; - [condition unlock]; + uxEvent->Set(); }); // Action - validate this action takes effect on the control [buttonVC textTitleNormal].text = expectedString; }); - [condition lock]; - ASSERT_TRUE_MSG(signaled || [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:c_testTimeoutInSec]], - "FAILED: Waiting for property changed event timed out!"); - [condition unlock]; + ASSERT_TRUE_MSG(uxEvent->Wait(c_testTimeoutInSec), "FAILED: Waiting for property changed event timed out!"); } diff --git a/tests/functionaltests/UXTestHelpers.h b/tests/functionaltests/UXTestHelpers.h index f52132fe72..446e0a6420 100644 --- a/tests/functionaltests/UXTestHelpers.h +++ b/tests/functionaltests/UXTestHelpers.h @@ -44,6 +44,9 @@ UIWindow* GetCurrentWindow(); NSString* NSStringFromPropertyValue(RTObject* rtPropertyValue); NSString* NSStringFromPropertyValue(const Microsoft::WRL::ComPtr& inspPropertyValue); +// WUXMSolidColorBrush helper +bool IsRGBAEqual(WUXMSolidColorBrush* brush, UIColor* color); + // Class that is used to display and dismiss the viewController's view within the test class ViewControllerPresenter { public: @@ -81,4 +84,34 @@ class XamlEventSubscription { StrongId _propertyToObserve; }; // class XamlEventSubscription +// Class used to signal and wait during a test run +class UXEvent { +public: + enum EventType { Manual, AutoReset }; + + static std::shared_ptr CreateManual() { + return std::make_shared(Manual); + } + + static std::shared_ptr CreateAuto() { + return std::make_shared(AutoReset); + } + + UXEvent(EventType eventType) : _eventType(eventType), _signaled(false) { + _condition.attach([[NSCondition alloc] init]); + } + + UXEvent(const UXEvent& other) = delete; // no copy + UXEvent& operator=(const UXEvent& other) = delete; + + void Set(); + void Reset(); + bool Wait(int timeoutInSeconds); + +private: + bool _signaled; + EventType _eventType; + StrongId _condition; +}; // class UXEvent + } // namespace UXTestAPI diff --git a/tests/functionaltests/UXTestHelpers.mm b/tests/functionaltests/UXTestHelpers.mm index aefc851b21..5c8eeb78aa 100644 --- a/tests/functionaltests/UXTestHelpers.mm +++ b/tests/functionaltests/UXTestHelpers.mm @@ -45,6 +45,14 @@ return nil; } +bool IsRGBAEqual(WUXMSolidColorBrush* brush, UIColor* color) { + CGFloat red, green, blue, alpha; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + + return [brush.color r] == (int)(red * 255) && [brush.color g] == (int)(green * 255) && [brush.color b] == (int)(blue * 255) && + [brush.color a] == (int)(alpha * 255); +} + // // ViewControllerPresenter methods // @@ -106,4 +114,37 @@ } } +// +// UXEvent methods +// +void UXEvent::Set() { + [_condition lock]; + _signaled = true; + [_condition signal]; + [_condition unlock]; +} + +void UXEvent::Reset() { + [_condition lock]; + _signaled = false; + [_condition unlock]; +} + +bool UXEvent::Wait(int timeOutInSeconds) { + [_condition lock]; + + if (!_signaled) { + _signaled = [_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeOutInSeconds]] ? true : false; + } + + bool waitSignal = _signaled; + [_condition unlock]; + + if (_eventType == AutoReset) { + Reset(); + } + + return waitSignal; +} + } // namespace UXTestAPI