test_xpath.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """Created on Thu Apr 04 2024 16:41:25 by codeskyblue
  4. """
  5. import pytest
  6. from unittest.mock import Mock
  7. from PIL import Image
  8. from uiautomator2.xpath import XMLElement, XPath, XPathSelector, XPathEntry, XPathElementNotFoundError, convert_to_camel_case, is_xpath_syntax_ok, safe_xmlstr, str2bytes, strict_xpath
  9. mock = Mock()
  10. mock.screenshot.return_value = Image.new("RGB", (1080, 1920), "white")
  11. mock.dump_hierarchy.return_value = """<?xml version="1.0" encoding="UTF-8"?>
  12. <hierarchy rotation="0">
  13. <node index="0" text="" resource-id="android:id/content" class="FrameLayout" content-desc="" bounds="[0,0][1080,1920]">
  14. <node index="0" text="n1" resource-id="android:id/text1" class="TextView" content-desc="" bounds="[0,0][1080,100]" />
  15. <node index="1" text="n2" resource-id="android:id/text2" class="TextView" content-desc="" bounds="[0,100][1080,200]" />
  16. </node>
  17. <node index="1" text="" resource-id="android:id/statusBarBackground" class="android.view.View" package="com.android.systemui" content-desc="" bounds="[0,0][1080,24]" />
  18. </hierarchy>
  19. """
  20. x = XPathEntry(mock)
  21. def test_safe_xmlstr():
  22. for input, expect in [
  23. ('android.widget.TextView', 'android.widget.TextView'),
  24. ('test$123', 'test.123'),
  25. ('$@#&123.456$', '123.456'),
  26. ]:
  27. assert safe_xmlstr(input) == expect
  28. def test_str2bytes():
  29. assert str2bytes(b'123') == b'123'
  30. assert str2bytes('123') == b'123'
  31. def test_is_xpath_syntax_ok():
  32. assert is_xpath_syntax_ok("/a")
  33. assert is_xpath_syntax_ok("//a")
  34. assert is_xpath_syntax_ok("//a[@text='b]") is False
  35. assert is_xpath_syntax_ok("//a[") is False
  36. def test_convert_to_camel_case():
  37. assert convert_to_camel_case("hello-world") == "helloWorld"
  38. def test_strict_xpath():
  39. for (input, expect) in [
  40. ("@n1", "//*[@resource-id='n1']"),
  41. ("//TextView", "//TextView"),
  42. ("//TextView[@text='n1']", "//TextView[@text='n1']"),
  43. ("(//TextView)[2]", "(//TextView)[2]"),
  44. ("//TextView/", "//TextView"), # test rstrip /
  45. ]:
  46. assert strict_xpath(input) == expect
  47. def test_XPath():
  48. xp = XPath("//TextView")
  49. assert xp == "//TextView"
  50. assert xp.joinpath("/n1") == "//TextView/n1"
  51. def test_xpath_selector():
  52. assert isinstance(x("n1"), XPathSelector)
  53. assert isinstance(x("//TextView"), XPathSelector)
  54. xp1 = x("n1")
  55. xp2 = xp1.child("n2")
  56. # xp1 should not be changed
  57. assert xp1.get(timeout=0).text == "n1"
  58. assert xp1.get_text() == "n1"
  59. # match return None or XMLElement
  60. assert xp1.match() is not None
  61. assert xp2.match() is None
  62. def test_xpath_with_instance():
  63. # issue: https://github.com/openatx/uiautomator2/issues/941
  64. el = x('(//TextView)[2]').get(0)
  65. assert el.text == "n2"
  66. def test_xpath_click():
  67. x("n1").click()
  68. assert mock.click.called
  69. assert mock.click.call_args[0] == (540, 50)
  70. mock.click.reset_mock()
  71. assert x("n1").click_exists() == True
  72. assert mock.click.call_args[0] == (540, 50)
  73. mock.click.reset_mock()
  74. assert x("n3").click_exists(timeout=.1) == False
  75. assert not mock.click.called
  76. def test_xpath_exists():
  77. assert x("n1").exists
  78. assert not x("n3").exists
  79. def test_xpath_wait_and_wait_gone():
  80. assert x("n1").wait() is True
  81. assert x("n3").wait(timeout=.1) is False
  82. assert x("n3").wait_gone(timeout=.1) is True
  83. assert x("n1").wait_gone(timeout=.1) is False
  84. def test_xpath_get():
  85. assert x("n1").get().text == "n1"
  86. assert x("n2").get().text == "n2"
  87. with pytest.raises(XPathElementNotFoundError):
  88. x("n3").get(timeout=.1)
  89. def test_xpath_all():
  90. assert len(x("//TextView").all()) == 2
  91. assert len(x("n3").all()) == 0
  92. assert len(x("n1").all()) == 1
  93. el = x("n1").all()[0]
  94. assert isinstance(el, XMLElement)
  95. assert el.text == "n1"
  96. def test_xpath_element():
  97. el = x("n1").get(timeout=0)
  98. assert el.text == "n1"
  99. assert el.center() == (540, 50)
  100. assert el.offset(0, 0) == (0, 0)
  101. assert el.offset(1, 1) == (1080, 100)
  102. assert el.screenshot().size == (1080, 100)
  103. assert el.bounds == (0, 0, 1080, 100)
  104. assert el.rect == (0, 0, 1080, 100)
  105. assert isinstance(el.info, dict)
  106. assert el.get_xpath(strip_index=True) == "/hierarchy/FrameLayout/TextView"
  107. mock.click.reset_mock()
  108. el.click()
  109. assert mock.click.called
  110. assert mock.click.call_args[0] == (540, 50)
  111. mock.long_click.reset_mock()
  112. el.long_click()
  113. assert mock.long_click.called
  114. assert mock.long_click.call_args[0] == (540, 50)
  115. mock.swipe.reset_mock()
  116. el.swipe("up")
  117. assert mock.swipe.called