How do we add selectors/ids to Flutter widgets so they can be accessed from Appium - flutter

we want to use Appium/Selenium to do automated testing on a Flutter application. Some elements do not have selectors when viewed in Selenium. In Android we just add ids onto every element and they appear in Appium. How do we do this in a flutter environment?

I found an approach with a workaround which then lets you use Selenium reasonably naturally with Flutter Web (although not working with headless browser)
You need to find the offset of window x y coordinates from screen x y coordiantes. I found this idea in another thread
pageCallibrator.html:
<script>
window.coordinates = [];
document.addEventListener('click', function() {
window.coordinates = [event.pageX, event.pageY];
});
</script>
Then in Selenium setup before running tests (Java example)
int windowScreenOffsetX = 0;
int windowScreenOffsetY = 0;
void callibrateXY(WebDriver driver) {
driver.get("http://localhost:8080/pageCallibrator.html"); //TODO adjust host
Dimension size = driver.manage().window().getSize();
int x = size.width / 2;
int y = size.height / 2;
clickMouseAtXY(x, y);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
List<Object> coordinates = (List<Object>) ((JavascriptExecutor) driver).executeScript("return window.coordinates;");
windowScreenOffsetX = x - (int) (long) coordinates.get(0);
windowScreenOffsetY = y - (int) (long) coordinates.get(1);
}
Now in Selenium to press a Flutter button
WebElement continueToBankButtonElement = findElementWithText(driver, "My button text");
clickMouseAtElement(continueToBankButtonElement);
where you define
import org.openqa.selenium.*
Robot robot = new Robot();
Driver driver = new ChromeDriver(options); // TODO handler exceptions and options in a method
WebElement findElementWithText(WebDriver driver, String text) {
return driver.findElement(containsTextLocator(text));
}
By containsTextLocator(String text) {
return By.xpath("//*[contains(text(), '" + text + "')]");
}
void clickMouseAtElement(WebElement element) {
clickMouseAtXY(element.getLocation().getX() + element.getSize().width / 2, element.getLocation().getY() + element.getSize().height / 2);
}
void clickMouseAtXY(int x, int y) {
moveMouse(x, y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
}
/**
* #param x
* #param y
*/
protected void moveMouse(int x, int y) {
robot.mouseMove(x + windowScreenOffsetX, y + windowScreenOffsetY); // Offset of page from screen
}

Prior to this morning I knew nothing of Flutter. A few hours later and I can safely say "you don't." While Flutter makes developing an application quick and easy, it removes a lot of the control you have, including the level of customization you're looking for.
There are hits on this on official Flutter message boards dating back a year or two, but there were no answers.
You could attempt locating everything by text? Kluge, difficult or impossible to maintain, but likely your only option at this point.

Related

how can i synchronize between the screenshot and the extraction of gameObject location in Unity?

I’m trying to create data for detection.
not on android.
I’m moving an object on screen and every X seconds I’m saving a screenshot and the coordinate of the object on a json file.
The problem is that the screenshot doesn’t happening in the exact time I’m asking it to but the coordinate finding and json file creation is.
I managed to solve this problem by waiting a fixed frame number between each of the operation.
public static class WaitFor
{
public static IEnumerator Frames(int frameCount)
{
while (frameCount > 0)
{
frameCount--;
yield return null;
}
}
}
public IEnumerator CoroutineAction()
{
yield return StartCoroutine(WaitFor.Frames(3));
}
public void Save()
{
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
CoroutineAction();
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
This solution somewhat works but it still not the exact location of the nodes and I would appreciate it if someone would know a better and cleaner solution.
Another problem I have is that if I try to use the super-resolution option
Example :
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png" , 4);
And multiply the location vector by 4.
The locations will not be in the location of the object in the 2D image, not even close. Which doesn’t make any sense and It would really help me if I could create a bigger image without the use of external libraries to extend my picture by 4 (which work perfectly fine).
Might not be answering your question so far but what I meant with the comment is
currently you 1. use the wait coroutine wrong and 2. it doesn't do anything
To 1.:
public void Save()
{
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
CoroutineAction();
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
What happens actually is:
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
is executed immediately. Than "parallel" (not really ofcourse) the coroutine would run if you used StartCoroutine(CoroutineAction());
But now to 2.:
the routine
public IEnumerator CoroutineAction()
{
yield return StartCoroutine(WaitFor.Frames(3));
}
does absolutetly nothing than waiting 3 frames ...
I guess what you wanted to do instead is something like
// actually start a coroutine
public void Save()
{
StartCoroutine(SaveRoutine());
}
// optional parameter superSize => if not provided uses 1
private IEnumerator SaveRoutine(int superSize = 1)
{
// makes sure the factor is always >= 1
var sizeFactor = Mathf.Max(1,superSize);
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png", sizeFactor);
//Wait for 3 frames
// I think this makes more sense than your dedicated class with the static method
for (int i = 0; i < 4; i++)
{
yield return null;
}
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position) * sizeFactor;
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
Than as also said already I'm not sure because I can't tell if you should get the location in the frame where you start the capture or in the frame where you end. But I guess you rather should get the location beforehand and than start the capture:
// optional parameter superSize => if not provided uses 1
private IEnumerator SaveRoutine(int superSize = 1)
{
// make sure the factor is always at least 1
var sizeFactor = Mathf.Max(1,superSize);
var location = Camera.main.WorldToScreenPoint(gameObject.transform.position) * sizeFactor;
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png", sizeFactor);
//Wait for 3 frames
// I think this makes more sense than your dedicated class with the static method
for (int i = 0; i < 4; i++)
{
yield return null;
}
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
Also note that as mentioned in the linked duplicate
The CaptureScreenshot returns immediately on Android. The screen capture continues in the background. The resulting screen shot is saved in the file system after a few seconds.
You are waiting 3 Frames .. that's usully not even a 1/10 of a second (depending on the frame rate obviously).
You should rather wait for actual seconds using WaitForSeconds e.g.
yield return new WaitForSeconds(3);
to wait e.g. 3 seconds. BUT also note that this will not work in combination with suggested Time.timeScale since WaitForSecodns depends on the timeScale as well.
I think you also should not / don't need to use
#".\" + counter + ".png"
in the filename but simply
counter + ".png"
since
On mobile platforms the filename is appended to the persistent data path. See Application.persistentDataPath for more information.
Try to pause your movements like this:
Time.timeScale = 0;
(Game loop continues)
You can wait some frames/time, or see if you get some feedback when the screenshot is done.
This works best if you use Time.deltaTime in your Update() method which is recommended anyway.

How can I determine the screen size using addon SDK?

How can I get the screen size using with addon SDK ?
var w = screen.width/2;
gives me an error : Message: ReferenceError: screen is not defined
You can use the window you have associated to your add-on; it's probably safer, because it will work even if the last visible window is closed but firefox is still opened (e.g. on OS X):
const { window: { screen }} = require("sdk/addon/window");
console.log(screen.width);
This will work:
var screen = require('sdk/window/utils').getMostRecentBrowserWindow().screen;
console.log(screen.width);
If you want multi monitor support I have a script but you have to understand it. It uses XPCOM, and it needs a range. This script only checks along the x axis, you should also check along the y axis.
So this is the script here that will detect all monitors in the x plane IF it falls in the y plane of 0-20 coordintates of primary screen, I don't recommend this method.
var sm = Cc['#mozilla.org/gfx/screenmanager;1'].getService(Ci.nsIScreenManager);
function getScreens() {
var screen = null;
var screens = [];
var screenManager = sm;
var min = 0;
var max = 0;
for (x = 0; x < 15000; x += 600) {
var s = screenManager.screenForRect(x, 20, 10, 10);
if (s != screen) {
screen = s;
var left = {},
top = {},
width = {},
height = {};
screenManager.primaryScreen.GetRect(left, top, width, height);
screens.push({
width: width.value,
height: height.value,
min: min,
max: min + width.value
});
min += width.value;
}
}
return screens;
}
var screens = getScreens();
console.log('screens:', screens);
This is the method I recommend
I needed to detect all monitor dimensons and had to resort to jsctypes, if you need that its here: https://github.com/Noitidart/NativeShot/blob/master/modules/workers/MainWorker.js#L853-L1523
That code is extremely long, thats because its getting all monitors and the taking screenshots of them. So you will want to extract just the monitors part. If you need help with it I can do it for you.

Why comparing float values is such difficult?

I am newbie in Unity platform. I have 2D game that contains 10 boxes vertically following each other in chain. When a box goes off screen, I change its position to above of the box at the top. So the chain turns infinitely, like repeating Parallax Scrolling Background.
But I check if a box goes off screen by comparing its position with a specified float value. I am sharing my code below.
void Update () {
offSet = currentSquareLine.transform.position;
currentSquareLine.transform.position = new Vector2 (0f, -2f) + offSet;
Vector2 vectorOne = currentSquareLine.transform.position;
Vector2 vectorTwo = new Vector2 (0f, -54f);
if(vectorOne.y < vectorTwo.y) {
string name = currentSquareLine.name;
int squareLineNumber = int.Parse(name.Substring (11)) ;
if(squareLineNumber < 10) {
squareLineNumber++;
} else {
squareLineNumber = 1;
}
GameObject squareLineAbove = GameObject.Find ("Square_Line" + squareLineNumber);
offSet = (Vector2) squareLineAbove.transform.position + new Vector2(0f, 1.1f);
currentSquareLine.transform.position = offSet;
}
}
As you can see, when I compare vectorOne.y and vectorTwo.y, things get ugly. Some boxes lengthen and some boxes shorten the distance between each other even I give the exact vector values in the code above.
I've searched for a solution for a week, and tried lots of codes like Mathf.Approximate, Mathf.Round, but none of them managed to compare float values properly. If unity never compares float values in the way I expect, I think I need to change my way.
I am waiting for your godlike advices, thanks!
EDIT
Here is my screen. I have 10 box lines vertically goes downwards.
When Square_Line10 goes off screen. I update its position to above of Square_Line1, but the distance between them increases unexpectedly.
Okay, I found a solution that works like a charm.
I need to use an array and check them in two for loops. First one moves the boxes and second one check if a box went off screen like below
public GameObject[] box;
float boundary = -5.5f;
float boxDistance = 1.1f;
float speed = -0.1f;
// Update is called once per frame
void Update () {
for (int i = 0; i < box.Length; i++) {
box[i].transform.position = box[i].transform.position + new Vector3(0, speed, 0);
}
for (int i = 0; i < box.Length; i++)
{
if(box[i].transform.position.y < boundary)
{
int topIndex = (i+1) % box.Length;
box[i].transform.position = new Vector3(box[i].transform.position.x, box[topIndex].transform.position.y + boxDistance, box[i].transform.position.z);
break;
}
}
}
I attached it to MainCamera.
Try this solution:
bool IsApproximately(float a, float b, float tolerance = 0.01f) {
return Mathf.Abs(a - b) < tolerance;
}
The reason being that the tolerances in the internal compare aren't good to use. Change the tolerance value in a function call to be lower if you need more precision.

How to translate x, y origin of a PDPage?

I'm new to Apache's PDFBox. I'm using version 2.0.0, taken from the SVN repository.
Actually I try to increase the cropbox of a PDPage object. OK, no challenge. But the page content remains in the lower left corner of the cropbox. I want it centered in the new cropbox space.
I understood that all content is positioned absolutely in PDF. So my question: Is there a way using PDFBox to translate the origin (x, y) of my PDPage or the content elements?
Regards
Hans
The first approach would be to simply change the crop box like this:
PDDocument document = PDDocument.load(new File("data/test.pdf"));
PDDocumentCatalog catalog = document.getDocumentCatalog();
#SuppressWarnings("unchecked")
List<PDPage> pages = catalog.getAllPages();
float expand = 72;
for (PDPage page : pages)
{
PDRectangle cropBox = page.findCropBox();
PDRectangle newCropBox = new PDRectangle();
newCropBox.setLowerLeftX(cropBox.getLowerLeftX() - expand);
newCropBox.setLowerLeftY(cropBox.getLowerLeftY() - expand);
newCropBox.setUpperRightX(cropBox.getUpperRightX() + expand);
newCropBox.setUpperRightY(cropBox.getUpperRightY() + expand);
page.setCropBox(newCropBox);
}
document.save("data/out/test-expand-crop-simple.pdf");
This only works sometimes, though, because according to the specification ISO 32000-1, section 14.11.2 Page Boundaries
The crop, bleed, trim, and art boxes shall not ordinarily extend beyond the boundaries of the media box. If they do, they are effectively reduced to their intersection with the media box.
(also see this answer)
Thus, we have to make sure that the crop box even after enlarging still fits into the media box, e.g. like this:
PDDocument document = PDDocument.load(new File("data/test.pdf"));
PDDocumentCatalog catalog = document.getDocumentCatalog();
#SuppressWarnings("unchecked")
List<PDPage> pages = catalog.getAllPages();
float expand = 72;
for (PDPage page : pages)
{
PDRectangle cropBox = page.findCropBox();
PDRectangle newCropBox = new PDRectangle();
newCropBox.setLowerLeftX(cropBox.getLowerLeftX() - expand);
newCropBox.setLowerLeftY(cropBox.getLowerLeftY() - expand);
newCropBox.setUpperRightX(cropBox.getUpperRightX() + expand);
newCropBox.setUpperRightY(cropBox.getUpperRightY() + expand);
page.setCropBox(newCropBox);
PDRectangle mediaBox = page.findMediaBox();
PDRectangle newMediaBox = new PDRectangle();
newMediaBox.setLowerLeftX(mediaBox.getLowerLeftX() - expand);
newMediaBox.setLowerLeftY(mediaBox.getLowerLeftY() - expand);
newMediaBox.setUpperRightX(mediaBox.getUpperRightX() + expand);
newMediaBox.setUpperRightY(mediaBox.getUpperRightY() + expand);
page.setMediaBox(newMediaBox);
}
document.save("data/out/test-expand-crop-and-media.pdf");
The central code to manipulate the crop box looks as follows:
#Override
protected void treeNodeChanged(PDFTreeNode node)
{
if (node instanceof PDFFloatNode)
{
PDFFloatNode nodeF = (PDFFloatNode)node;
String strDataKey = node.getDataKey();
if ("x".equals(strDataKey))
{
m_rect.setLowerLeftX(nodeF.getFloat());
}
else if ("y".equals(strDataKey))
{
m_rect.setLowerLeftY(nodeF.getFloat());
}
else if ("width".equals(strDataKey))
{
m_rect.setUpperRightX(nodeF.getFloat());
}
else if ("height".equals(strDataKey))
{
m_rect.setUpperRightY(nodeF.getFloat());
}
}
if (m_parent != null)
{
m_parent.treeNodeChanged(node);
}
}
Where m_rect is an instance of PDRectangle. In fact this information doesn't help you, #mkl. In between I found an information to solve the problem (I hope). There is an operation which is obviously not known in PDFBox.
<</PageOffset [-20 20]>> setpagedevice
Now I'm looking for a way to implement it into the PDF.
Thanks
Hans
The Pageoffset dictionary entry was not very helpful. It can be used as a parameter for GhostScript. But GS does not really create a dictionary entry. Instead it offsets the content of all pages. A capability which doesn't exist in PDFBox.
I found a solution using LayerUtility:
PDDocument docIn = null;
try
{
docIn = PDDocument.load("./pdf/Test1.pdf");
float fBorder = 10 * MM_TO_UNITS; // Arbitrary 10 mm
PDDocument docOut = new PDDocument();
PDPage pageIn = (PDPage)docIn.getDocumentCatalog().getPages().getKids().get(0);
PDRectangle rectCrop = pageIn.findCropBox();
PDPage pageClone = clonePage(docOut, pageIn, true),
pageOut = new PDPage(
new PDRectangle(rectCrop.getWidth() + 2 * fBorder, rectCrop.getHeight() + 2 * fBorder)
);
docOut.addPage(pageOut);
PDPageContentStream stream = new PDPageContentStream(docOut, pageOut);
stream.close();
LayerUtility lu = new LayerUtility(docOut);
lu.wrapInSaveRestore(pageOut);
PDXObjectForm xobj = lu.importPageAsForm(docIn, pageClone);
AffineTransform at = new AffineTransform();
// That's the point where x,y offset takes place
at.setToTranslation(fBorder, fBorder);
lu.appendFormAsLayer(pageOut, xobj, at, "layerx");
docOut.addPage(pageOut);
docOut.save("./pdf/Test1out.pdf");
}
finally
{
if (docIn != null)
{
docIn.close();
}
}
I'm not very happy with this. Because it changes the page structure. But at least I have a solution.
Regards
Hans
To increase the CropBox, you set the MediaBox like this:
PDRectangle box = new PDRectangle(pageWidth, pageHeight);
page.setMediaBox(box); // MediaBox > BleedBox > TrimBox/CropBox

GWT Query DropEvent: can I get the drop coordinates?

I'm using GWT and GWT Query plugin for my web application. I have a draggable image and a GWT Canvas wrapped in a Droppable. My code looks like this:
final Canvas canvas = Canvas.createIfSupported();
canvas.setHeight("340px");
DroppableWidget<Canvas> test = new DroppableWidget<Canvas>(canvas);
test.addDropHandler(new DropEventHandler() {
#Override
public void onDrop(DropEvent event) {
Context2d context = canvas.getContext2d();
Image img = new Image("image");
ImageElement i = ImageElement.as(img.getElement());
//get x and y coordinates out of the drop event
context.drawImage(i, x, y);
}
});
The question is... how can I get these x and y coordinates out of the drop event so I can draw image on the canvas on the drop place?
First you should retrieve your image element via the DropEvent
ImageElement i = ImageElement.as(event.getDraggable());
After that if you want to know where the draggable is dropped (related to the droppable), you can calculate the coordinate like this :
Offest droppableOffset = $(event.getDroppable()).offset();
Offset draggableOffset = $(event.getDraggable()).offset();
int x = draggableOffset.left - droppableOffset.left;
int y = draggableOffset.top - droppableOffset.top;