iOS 13 Xcode UI test Automation type mismatch - ios13

My app uses a WKWebView that is setup in code (this is done because of this bug in iOS 10):
final class HelpViewController: UIViewController {
// …
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(webView)
let margins = view.layoutMarginsGuide
webView.topAnchor.constraint(equalTo: self.back.bottomAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: margins.rightAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: margins.leftAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: margins.bottomAnchor).isActive = true
webView.navigationDelegate = self
//…
}
My UI test looks like this:
func test_helpButtonShowsHelpText() {
//…
let webView = shopEasyApp.webViews.element
let webViewExists = webView.waitForExistence(timeout: kDefaultUITestTimeout)
XCTAssert(webViewExists, "Web view does not exist")
let webViewIsHittable = webView.isHittable
//…
}
This test run without problems up to iOS 12.
With iOS 13, it stops at the test webView.isHittable with the following error:
Failed to get matching snapshot: Multiple matching elements found for <XCUIElementQuery: 0x600000bdfbb0>.
The log says:
Assertion Failure: ShopEasyBasicUITests.swift:1100: Failed to get matching snapshot: Multiple matching elements found for <XCUIElementQuery: 0x600003efb4d0>.
Sparse tree of matches:
→Application, pid: 71038, label: 'Shop Easy!'
↳Window, {{0.0, 0.0}, {375.0, 812.0}}
↳Other, {{0.0, 0.0}, {375.0, 812.0}}
↳Other, {{0.0, 0.0}, {375.0, 812.0}}
↳WebView, {{16.0, 74.0}, {343.0, 704.0}}
↳WebView, {{16.0, 74.0}, {343.0, 704.0}}
↳WebView, {{16.0, 74.0}, {343.0, 704.0}}
Possibly caused by runtime issues:
Automation type mismatch: computed WebView from legacy attributes vs ScrollView from modern attribute. Input attributes and values: {
"XC_kAXXCAttributeAutomationType" = 46;
"XC_kAXXCAttributeElementBaseType" = UIScrollView;
"XC_kAXXCAttributeElementType" = WKScrollView;
"XC_kAXXCAttributeTraits" = 8589934592;
}
Automation type mismatch: computed Other from legacy attributes vs WebView from modern attribute. Input attributes and values: {
"XC_kAXXCAttributeAutomationType" = 58;
"XC_kAXXCAttributeElementBaseType" = UIView;
"XC_kAXXCAttributeElementType" = WKWebView;
"XC_kAXXCAttributeTraits" = 146028888064;
}
Automation type mismatch: computed Other from legacy attributes vs WebView from modern attribute. Input attributes and values: {
"XC_kAXXCAttributeAutomationType" = 58;
"XC_kAXXCAttributeElementBaseType" = UIView;
"XC_kAXXCAttributeElementType" = WKContentView;
"XC_kAXXCAttributeTraits" = 146028888064;
}
The view hierarchy is the following:
My questions are:
What is wrong with my code, and how to do it correctly?

You have three WebKit views in play which XCTest can see:
WKScrollView
WKWebView
WKContentView
It looks like they have changed the way that elements are given their type on iOS 13, meaning that your view hierarchy now contains multiple elements which resolve to being classed as a WebView. It appears that WKWebView and WKContentView now both resolve to being elements of type WebView, whereas in iOS 12, only the WKScrollView (which, in iOS 13, no longer resolves to being classed as a WebView element) was classed as a WebView.
Now for the query:
shopEasyApp.webViews.element
The query uses element, which is only supposed to be used when you know the query will resolve to a single element. Since there are now 2 results for shopEasyApp.webViews, there is not a single element for element to return. You are able to check for existence because checking XCUIElement's exists property does not require the query to be resolved successfully. However, all other XCUIElement properties do require the query to be resolved, so when isHittable is used, the XCTest engine attempts to resolve the query, finds that the query does not resolve to a single element as expected, and throws this error.
Multiple matching elements found for <XCUIElementQuery: 0x600003efb4d0>
To resolve this, I suggest you modify the query to use the first matching index instead of using element:
shopEasyApp.webViews.elementBound(by: 0)
In the event that there is more than a single WebView element on screen, this query will choose the first element in the list.

I have faced similar issue - there were no webview's descendants.
After investigation it turned out that everything is OK on iOS 13.1 but not on iOS 13.3.
I think it's a simulator's bug.
For all facing the same problem - specify earlier version (13.1) until it's fixed.

Related

Xamarin Forms (VS2019) + iOS Intents UI: How to access Assets from Extension (IntentsUI)

I used the Xamarin version of SoupChef for Siri Intents. I was able to access the Container's Assets from the SoupChefIntentsUI.IntentViewController by requesting the bundle by identifier (using the BundleIdentifier of the main App) and then I just loaded the image by passing the bundle
CGSize DisplayOrderConfirmation(Order order, OrderSoupIntent intent, OrderSoupIntentResponse response){
/* unrelated code */
//this line work in the SoupChef example but
//On Xamarin.Forms this returns null
var containerBundle = NSBundle.FromIdentifier("com.something.SoupChef");
//always returns null because it seems it looks into the IntentsUI.Assets
var iconNull = UIImage.FromBundle("AppIcon");
//it returns the icon from the SoupChef.Assets
var iconNotNull = UIImage.FromBundle("AppIcon", containerBundle, configuration: null);
/* unrelated code */
}
I was also able to retrieve the AppIcon by creating the NSBundle doing something like this (in case you didn't want to assume that the bundle identifier names don't follow the Apple standard where the Container and the extensions have the same bundle identifier with the exception of the last segment)
CGSize DisplayOrderConfirmation(Order order, OrderSoupIntent intent, OrderSoupIntentResponse response){
/* unrelated code */
Class GetClassForType (Type type)
{
IntPtr myClassHandle = Class.GetHandle (typeToLookup);
if (myClassHandle != IntPtr.Zero)
return new Class (myClassHandle);
else
return null;
}
//this returns the bundle identifier of the SoupChef app
//(not the SoupChefIntentUI) on the SoupChef example
//on Xamarin.Forms (my project) it returns the IntentsUI identifier
var containerBundle = NSBundle.FromClass(GetClassForType(typeof(SoupChef.OrderIntent)));
//the icon is returned
var icon = UIImage.FromBundle("AppIcon", containerBundle, configuration: null);
/* unrelated code */
}
My problem is that I want to do the same thing on a different project that is using Xamarin.Forms, and the two previous ways that worked for me on the SoupChef project don't work here.
Is there a way to access the Assets set on the App.iOS.Assets or do I have to move them to the shared project where I have the models and other things that both the App.iOS and its Extensions are using?
I noticed that the Bindings (the project where the OrderIntent is) in the SoupChef example has the same namespace as the Container App ("SoupChef"), so I assigned the same namespace in my project with Xamarin.Forms and still nothing.

How to check tag of view in which touch occurred

I am developing a Xamarin.Forms application targeting the iOS platform. I want to only allow stylus input (i.e. disallow finger/direct input) in my entire app except for one single control (a SwitchCell).
For that, I implemented a custom UIApplication class and overrode the SendEvent(UIEvent uievent) method.
In SendEvent I am checking whether the event is a touch event using uievent.Type == UIEventType.Touches. Then I want to detect, if the touch occurred in the SwitchCell in which touch input should be allowed.
Because the SwitchCell is created in my Xamarin.Forms project as XAML, I implemented my own TaggableSwitchCell class inheriting from SwitchCell with a Tag property and registered a CustomRenderer that sets the Tag property of the UITableViewCell on iOS (which works as expected):
public class TaggableSwitchCellRenderer : SwitchCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
cell.Tag = (item as TaggableSwitchCell).Tag;
Log.Debug("Setting tag to " + cell.Tag);
return cell;
}
}
Then, in the SendEvent method, I check for the tag:
var isTouchAllowed = uievent.AllTouches.Any((touch) =>
{
var uitouch = touch as UITouch;
return uitouch.View != null && uitouch.View.ViewWithTag(Constants.Tag) != null;
});
Running this code on the device (iPad Pro 3rd Generation) works fine for every touch that is not in the SwitchCell. However, if the touch is in the SwitchCell (meaning isTouchAllowed should be true), the following message is printed (the app does not crash):
invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.
I know, that the problem is with the uitouch.View.ViewWithTag(Constants.Tag) != null statement, but I don't know how to solve it. I already tried manually checking the view tag using uitouch.View.Tag == Constants.Tag but this leads to the same problem.

This block contains invalid or unexpected content on Custom HTML

I am editing a document in draft mode Wordpress 5.2.2 in the Gutenberg editor, and add this Custom HTML block:
<pre><code class="language-typescript">const simple = &ltT&gt(cl: T) => cl;
class Hero {
constructor(public position: [number, number]) {}
}
interface { hello: number }
const errorOne = &ltT&gt(cl: T) => new cl(); // Cannot use 'new' with an expression whose type lacks a call or construct signature.</code></pre>
and it happily works as expected in preview. I save as draft.
When I return the HTML is ghosted and I get the error in the title. I can convert to HTML and it works again, but then it errors again when I return to it later.
It seems this error is talked about everywhere but the explanations are nonsense and resolve nothing.
If my Custom HTML is valid (which it seems to be), why does it work and then give an error. How do I fix this?
I think the main issue is not converting < & > properly in your code. They are missing the semicolon at the end of the string.
This code is working fine:
<pre><code class="language-typescript">const simple = <T>(cl: T) => cl;
class Hero {
constructor(public position: [number, number]) {}
}
interface { hello: number }
const errorOne = <T>(cl: T) => new cl(); // Cannot use 'new' with an expression whose type lacks a call or construct signature.</code></pre>
When you insert the code with missing semicolon, WordPress saved as is. However, when trying the load the page again WordPress compares the saved content (with missing characters) to the one generated from the block (which is probably attempting to display correct HTML). This process led to an error as both texts were not identical.
If you want to check the error yourself you can check the console through developer tool in your browser (F12 in Chrome).

App Engine emulated datastore - strange observation

I was experimenting with 'datastore' on my machine. Using this code.
Key parentKey = KeyFactory.createKey("parent", "parentId");
try {
// Entity parent = new Entity("parent", "parentId");
// parent.setUnindexedProperty("property1", "value1");
// ds.put(parent);
Entity savedParent = ds.get(parentKey);
// savedParent.setUnindexedProperty("property1", "value3");
// ds.put(savedParent);
// Entity child = new Entity("child", "childId", savedParent.getKey());
Entity child = ds.get(KeyFactory.createKey(savedParent.getKey(), "child", "childId"));
child.setUnindexedProperty("property1", "val2");
ds.put(child);
// logger.info("Saved child.");
} catch (EntityNotFoundException e) {
throw new RuntimeException(e);
}
First I saved parent entity and set property on it to "value2" then I added a child entity with property val1. Then I updated parent property to value3. Then I updated property on child to val2.
Then I found out in the admin console, that value of the property on the parent is back to value2. I repeated it again with the same result. Am I missing something? Or is this some kind of bug?
I suppose, this is manifestation of eventual consistency, right? I run each step in a new JVM instance, thinking that values must have been stored at the time I switched it off:-. Contradicting common sense, but correct in terms of emulation of eventual consistency....

Puzzling javascript array behavior

So, this may be a really stupid question, but I'm obviously missing something here.
Consider the following code:
var selectedItems = [];
selectedItems.push("0ce49e98-a8aa-46ad-bc25-3a49d475e9d3");
//fyi, selectedItems[selectedItems.length] = "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3"; produced the same result.
At the end selectedItems content looks like this:
Name Value Type
------------- -------------------------------------- ------
selectedItems {...} Object
- [0] "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3" String
- length 1 Long
But if I just try to call split() on the same string, like this:
selectedItems = "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3".split(",")
Now the content of my supposed array looks like this (missing length):
Name Value Type
------------- -------------------------------------- ------
selectedItems {...} Object
- [0] "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3" String
Any idea what the difference is? What's actually happening here?
Thanks in advance.
UPDATED:
I have a feeling there's actually something structurally different about the two resulting values, because (atlas) ajax chokes on the one with the length property when I try to pass it to a server-side WebMethod (no actual error message, but I know the call fails). I'm not sure.
UPDATE #2
I noticed that setting the targetLocationIdList this way, results in no 'length' property being displayed in Quick Watch window:
var params =
{
jobId : args.get_JobId(),
targetLocationIdList : retVal.split(',')
};
But this results contain 'length' property displayed in Quick Watch window:
var retValArr = [];
retValArr = retVal.split(',');
var params =
{
jobId : args.get_JobId(),
targetLocationIdList : retValArr
};
There isn't a difference at all programmatically. If you run your example in both chrome developers window and firebug it looks like the 2nd
Name Value
Type
------------- -------------------------------------- ------
selectedItems {...} Object
- [0] "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3" String
Length is an implied property
EDIT
var retVal = 'test';
var params =
{
jobId : 1,
targetLocationIdList : retVal.split(',')
};
console.log(params.targetLocationIdList.length) // prints 1
The code above prints 1 in IE8,Firefox,Chrome (in their dev tools or firebug) so think that this must be an issue with Visual Studio or with Atlas in the way that it shows the object.
Might this be a bug in the debugger? (Or is this causing problems in the browser?)
In the first instance, you are declaring selectedItems as an array and then assigning to it.
In the second instance, you are assigning to it directly from the split method.
I can only assume that VS takes the array declaration as a hint to display the length property.
It looks like you've found a defect in Microsoft's Javascript implementation. It's likely that Visual Studio and IE share the same implementation so that's why you're experiencing the error in just those two programs and not in Firefox or Chrome.
In the EMCAScript standard: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf
15.5.4.14 String.prototype.split (separator, limit)
Returns an Array object into which substrings of the result of converting this object to a string have been stored.
In the definition of Array Object:
15.4 Array Objects
... Every Array object has a length property whose value is always a nonnegative integer
less than 232.
What browser are you using, and are you sure there is no length property? I just did
var arr = "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3".split(",")
in my Firebug console, and arr does have a length property equal to 1, as expected.
How do you know the call fails? Make sure you run in debug mode, or use a failed handler to be notified of the reason.
Here's a very simple test:
var selectedItems = [];
selectedItems.push("0ce49e98-a8aa-46ad-bc25-3a49d475e9d3");
alert(selectedItems);
selectedItems = "0ce49e98-a8aa-46ad-bc25-3a49d475e9d3".split(",");
alert("boo");
alert(selectedItems.length);
After boo, we check if there is still a length.
Answer: yes.
So in other words, this is an error of Visual Studio, that and nothing more.

Resources