• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "widget_operator.h"
17 
18 namespace OHOS::uitest {
19     using namespace std;
20     using namespace nlohmann;
21 
22     static constexpr float SCROLL_MOVE_FACTOR = 0.7;
23 
IsScrolledToBorder(int oriDis,const std::vector<unique_ptr<Widget>> & allWidgets,const unique_ptr<Widget> & anchorLeafWidget,bool vertical)24     static bool IsScrolledToBorder(int oriDis,
25                                    const std::vector<unique_ptr<Widget>> &allWidgets,
26                                    const unique_ptr<Widget> &anchorLeafWidget,
27                                    bool vertical)
28     {
29         if (oriDis < 0) {
30             return false;
31         }
32         size_t index = 0;
33         for (; index < allWidgets.size(); ++index) {
34             if (allWidgets.at(index)->GetAttr(UiAttr::ACCESSIBILITY_ID) ==
35                 anchorLeafWidget->GetAttr(UiAttr::ACCESSIBILITY_ID)) {
36                 if (vertical) {
37                     return std::abs(allWidgets.at(index)->GetBounds().top_ - anchorLeafWidget->GetBounds().top_) <
38                         oriDis * SCROLL_MOVE_FACTOR;
39                 } else {
40                     return std::abs(allWidgets.at(index)->GetBounds().left_ - anchorLeafWidget->GetBounds().left_) <
41                         oriDis * SCROLL_MOVE_FACTOR;
42                 }
43             }
44         }
45         return false;
46     }
47 
ConstructNoFilterInWidgetSelector(WidgetSelector & scrollSelector,const std::string & hostApp,const std::string & hashCode)48     static void ConstructNoFilterInWidgetSelector(WidgetSelector &scrollSelector,
49                                                   const std::string &hostApp,
50                                                   const std::string &hashCode)
51     {
52         WidgetSelector parentStrategy;
53         WidgetMatchModel anchorModel2{UiAttr::HASHCODE, hashCode, ValueMatchPattern::EQ};
54         parentStrategy.AddMatcher(anchorModel2);
55         scrollSelector.AddAppLocator(hostApp);
56         auto error = ApiCallErr(NO_ERROR);
57         scrollSelector.AddParentLocator(parentStrategy, error);
58         scrollSelector.SetWantMulti(true);
59     }
60 
CalcFirstLeafWithInScroll(const std::vector<unique_ptr<Widget>> & widgetsInScroll)61     static int CalcFirstLeafWithInScroll(const std::vector<unique_ptr<Widget>>& widgetsInScroll)
62     {
63         std::string parentHie = "ROOT";
64         int index = -1;
65         for (auto &tempWid : widgetsInScroll) {
66             const std::string &curHie = tempWid->GetHierarchy();
67             if (curHie.find(parentHie) == std::string::npos) {
68                 break;
69             }
70             parentHie = curHie;
71             ++index;
72         }
73         return index;
74     }
75 
ConstructScrollFindSelector(const WidgetSelector & selector,const string & hashcode,const string & appName,ApiCallErr & error)76     static WidgetSelector ConstructScrollFindSelector(const WidgetSelector &selector, const string &hashcode,
77         const string &appName, ApiCallErr &error)
78     {
79         WidgetSelector newSelector = selector;
80         WidgetMatchModel anchorModel{UiAttr::HASHCODE, hashcode, ValueMatchPattern::EQ};
81         WidgetSelector parentSelector{};
82         parentSelector.AddMatcher(anchorModel);
83         newSelector.AddParentLocator(parentSelector, error);
84         newSelector.AddAppLocator(appName);
85         newSelector.SetWantMulti(false);
86         return newSelector;
87     }
88 
WidgetOperator(UiDriver & driver,const Widget & widget,const UiOpArgs & options)89     WidgetOperator::WidgetOperator(UiDriver &driver, const Widget &widget, const UiOpArgs &options)
90         : driver_(driver), widget_(widget), options_(options)
91     {
92     }
93 
GenericClick(TouchOp op,ApiCallErr & error) const94     void WidgetOperator::GenericClick(TouchOp op, ApiCallErr &error) const
95     {
96         DCHECK(op >= TouchOp::CLICK && op <= TouchOp::DOUBLE_CLICK_P);
97         auto retrieved = driver_.RetrieveWidget(widget_, error, true);
98         if (retrieved == nullptr || error.code_ != NO_ERROR) {
99             return;
100         }
101         const auto center = Point(retrieved->GetBounds().GetCenterX(), retrieved->GetBounds().GetCenterY(),
102             retrieved->GetDisplayId());
103         auto touch = OHOS::uitest::GenericClick(op, center);
104         driver_.PerformTouch(touch, options_, error);
105     }
106 
ScrollToEnd(bool toTop,ApiCallErr & error) const107     void WidgetOperator::ScrollToEnd(bool toTop, ApiCallErr &error) const
108     {
109         auto retrieved = driver_.RetrieveWidget(widget_, error, true);
110         if (retrieved == nullptr || error.code_ != NO_ERROR) {
111             return;
112         }
113         int turnDis = -1;
114         std::unique_ptr<Widget> lastTopLeafWidget = nullptr;
115         std::unique_ptr<Widget> lastBottomLeafWidget = nullptr;
116         while (true) {
117             auto hostApp = driver_.GetHostApp(widget_);
118             WidgetSelector selector{};
119             ConstructNoFilterInWidgetSelector(selector, hostApp, widget_.GetAttr(UiAttr::HASHCODE));
120             std::vector<unique_ptr<Widget>> widgetsInScroll;
121             driver_.FindWidgets(selector, widgetsInScroll, error, true);
122             if (error.code_ != NO_ERROR) {
123                 LOG_E("There is error when ScrollToEnd, msg is %{public}s", error.message_.c_str());
124                 return;
125             }
126             if (widgetsInScroll.empty()) {
127                 LOG_I("There is no child when ScrollToEnd");
128                 return;
129             }
130             if (toTop && IsScrolledToBorder(turnDis, widgetsInScroll, lastTopLeafWidget, true)) {
131                 return;
132             }
133             if (!toTop && IsScrolledToBorder(turnDis, widgetsInScroll, lastBottomLeafWidget, true)) {
134                 return;
135             }
136             if (toTop) {
137                 int index = CalcFirstLeafWithInScroll(widgetsInScroll);
138                 if (index < 0) {
139                     LOG_E("There is error when Find Widget's fist leaf");
140                     return;
141                 }
142                 lastTopLeafWidget = std::move(widgetsInScroll.at(index));
143                 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1));
144             } else {
145                 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1));
146             }
147             TurnPage(toTop, turnDis, true, error);
148         }
149     }
150 
DragIntoWidget(const Widget & another,ApiCallErr & error) const151     void WidgetOperator::DragIntoWidget(const Widget &another, ApiCallErr &error) const
152     {
153         auto widgetFrom = driver_.RetrieveWidget(widget_, error);
154         if (widgetFrom == nullptr || error.code_ != NO_ERROR) {
155             return;
156         }
157         auto boundsFrom = widgetFrom->GetBounds();
158         auto widgetTo = driver_.RetrieveWidget(another, error, false);
159         if (widgetTo == nullptr || error.code_ != NO_ERROR) {
160             return;
161         }
162         auto boundsTo = widgetTo->GetBounds();
163         auto centerFrom = Point(boundsFrom.GetCenterX(), boundsFrom.GetCenterY(), widgetFrom->GetDisplayId());
164         auto centerTo = Point(boundsTo.GetCenterX(), boundsTo.GetCenterY(), widgetTo->GetDisplayId());
165         auto touch = GenericSwipe(TouchOp::DRAG, centerFrom, centerTo);
166         driver_.PerformTouch(touch, options_, error);
167     }
168 
PinchWidget(float_t scale,ApiCallErr & error) const169     void WidgetOperator::PinchWidget(float_t scale, ApiCallErr &error) const
170     {
171         auto retrieved = driver_.RetrieveWidget(widget_, error);
172         if (retrieved == nullptr || error.code_ != NO_ERROR) {
173             return;
174         }
175         auto rectBound = retrieved->GetBounds();
176         if (scale < 0) {
177             error = ApiCallErr(ERR_INVALID_INPUT, "Please input the correct scale");
178             return;
179         }
180         auto touch = GenericPinch(rectBound, scale);
181         driver_.PerformTouch(touch, options_, error);
182     }
183 
InputText(string_view text,ApiCallErr & error) const184     void WidgetOperator::InputText(string_view text, ApiCallErr &error) const
185     {
186         auto retrieved = driver_.RetrieveWidget(widget_, error);
187         if (retrieved == nullptr || error.code_ != NO_ERROR) {
188             return;
189         }
190         auto origText = retrieved->GetAttr(UiAttr::TEXT);
191         if (origText.empty() && text.empty()) {
192             return;
193         }
194         static constexpr uint32_t focusTimeMs = 500;
195         static constexpr uint32_t typeCharTimeMs = 50;
196         vector<KeyEvent> events;
197         if (!origText.empty()) {
198             for (size_t index = 0; index < origText.size(); index++) {
199                 events.emplace_back(KeyEvent{ActionStage::DOWN, KEYCODE_DPAD_RIGHT, typeCharTimeMs});
200                 events.emplace_back(KeyEvent{ActionStage::UP, KEYCODE_DPAD_RIGHT, 0});
201                 events.emplace_back(KeyEvent{ActionStage::DOWN, KEYCODE_DEL, typeCharTimeMs});
202                 events.emplace_back(KeyEvent{ActionStage::UP, KEYCODE_DEL, 0});
203             }
204         }
205         const auto center = Point(retrieved->GetBounds().GetCenterX(), retrieved->GetBounds().GetCenterY(),
206             retrieved->GetDisplayId());
207         auto touch = OHOS::uitest::GenericClick(TouchOp::CLICK, center);
208         driver_.PerformTouch(touch, options_, error);
209         driver_.DelayMs(focusTimeMs); // short delay to ensure focus gaining
210         auto keyActionForDelete = KeysForwarder(events);
211         driver_.TriggerKey(keyActionForDelete, options_, error);
212         driver_.DelayMs(focusTimeMs);
213         driver_.InputText(text, error);
214     }
215 
ScrollFindWidget(const WidgetSelector & selector,bool vertical,ApiCallErr & error) const216     unique_ptr<Widget> WidgetOperator::ScrollFindWidget(const WidgetSelector &selector,
217                                                         bool vertical, ApiCallErr &error) const
218     {
219         auto retrieved = driver_.RetrieveWidget(widget_, error);
220         if (retrieved == nullptr || error.code_ != NO_ERROR) {
221             return nullptr;
222         }
223         bool scrollingUp = true;
224         int turnDis = -1;
225         std::unique_ptr<Widget> lastTopLeafWidget = nullptr;
226         std::unique_ptr<Widget> lastBottomLeafWidget = nullptr;
227         auto hostApp = driver_.GetHostApp(widget_);
228         auto newSelector = ConstructScrollFindSelector(selector, widget_.GetAttr(UiAttr::HASHCODE), hostApp, error);
229         while (true) {
230             std::vector<unique_ptr<Widget>> targetsInScroll;
231             driver_.FindWidgets(newSelector, targetsInScroll, error, true);
232             if (!targetsInScroll.empty()) {
233                 return std::move(targetsInScroll.at(0));
234             }
235             WidgetSelector scrollSelector;
236             ConstructNoFilterInWidgetSelector(scrollSelector, hostApp, widget_.GetAttr(UiAttr::HASHCODE));
237             std::vector<unique_ptr<Widget>> widgetsInScroll;
238             driver_.FindWidgets(scrollSelector, widgetsInScroll, error, false);
239             if (error.code_ != NO_ERROR) {
240                 LOG_E("There is error when Find Widget's subwidget, msg is %{public}s", error.message_.c_str());
241                 return nullptr;
242             }
243             if (widgetsInScroll.empty()) {
244                 LOG_I("There is no child when Find Widget's subwidget");
245                 return nullptr;
246             }
247             if (scrollingUp && IsScrolledToBorder(turnDis, widgetsInScroll, lastTopLeafWidget, vertical)) {
248                 scrollingUp = false;
249             } else if (IsScrolledToBorder(turnDis, widgetsInScroll, lastBottomLeafWidget, vertical)) {
250                 LOG_W("Scroll search widget failed: %{public}s", selector.Describe().data());
251                 return nullptr;
252             }
253             if (scrollingUp) {
254                 int index = CalcFirstLeafWithInScroll(widgetsInScroll);
255                 if (index < 0) {
256                     LOG_E("There is error when Find Widget's fist leaf");
257                     return nullptr;
258                 }
259                 lastTopLeafWidget = std::move(widgetsInScroll.at(index));
260                 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1));
261             } else {
262                 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1));
263             }
264             TurnPage(scrollingUp, turnDis, vertical, error);
265         }
266     }
267 
CheckDeadZone(bool vertical,ApiCallErr & error)268     bool WidgetOperator::CheckDeadZone(bool vertical, ApiCallErr &error)
269     {
270         auto bounds = widget_.GetBounds();
271         int maxDeadZone;
272         if (vertical) {
273             maxDeadZone = (bounds.bottom_ - bounds.top_) / TWO;
274         } else {
275             maxDeadZone = (bounds.right_ - bounds.left_) / TWO;
276         }
277         if (options_.scrollWidgetDeadZone_ >= maxDeadZone) {
278             error = ApiCallErr(ERR_INVALID_INPUT, "The offset is too large and exceeds the widget size.");
279             return false;
280         } else {
281             return true;
282         }
283     }
TurnPage(bool toTop,int & oriDistance,bool vertical,ApiCallErr & error) const284     void WidgetOperator::TurnPage(bool toTop, int &oriDistance, bool vertical, ApiCallErr &error) const
285     {
286         auto bounds = widget_.GetBounds();
287         Point topPoint;
288         Point bottomPoint;
289         auto screenSize = driver_.GetDisplaySize(error, widget_.GetDisplayId());
290         auto gestureZone = (vertical) ? screenSize.py_ / 20 : screenSize.px_ / 20;
291         topPoint = vertical ? Point(bounds.GetCenterX(), bounds.top_) : Point(bounds.left_, bounds.GetCenterY());
292         bottomPoint = vertical ? Point(bounds.GetCenterX(), bounds.bottom_) :
293             Point(bounds.right_, bounds.GetCenterY());
294         if (vertical) {
295             if (options_.scrollWidgetDeadZone_ > 0) {
296                 topPoint.py_ += options_.scrollWidgetDeadZone_;
297                 bottomPoint.py_ -= options_.scrollWidgetDeadZone_;
298             }
299             if (abs(screenSize.py_ - bottomPoint.py_) <= gestureZone) {
300                 bottomPoint.py_ = screenSize.py_ - gestureZone;
301             }
302             if (vertical && topPoint.py_ <= gestureZone) {
303                 topPoint.py_ = gestureZone;
304             }
305         } else {
306             if (options_.scrollWidgetDeadZone_ > 0) {
307                 topPoint.px_ += options_.scrollWidgetDeadZone_;
308                 bottomPoint.px_ -= options_.scrollWidgetDeadZone_;
309             }
310             if (abs(screenSize.px_ - bottomPoint.px_) <= gestureZone) {
311                 bottomPoint.px_ = screenSize.px_ - gestureZone;
312             }
313             if (topPoint.px_ <= gestureZone) {
314                 topPoint.px_ = gestureZone;
315             }
316         }
317         topPoint.displayId_ = widget_.GetDisplayId();
318         bottomPoint.displayId_ = widget_.GetDisplayId();
319         auto touch = (toTop) ? GenericSwipe(TouchOp::SWIPE, topPoint, bottomPoint)
320                              : GenericSwipe(TouchOp::SWIPE, bottomPoint, topPoint);
321         driver_.PerformTouch(touch, options_, error);
322         oriDistance = (vertical) ? std::abs(topPoint.py_ - bottomPoint.py_) : std::abs(topPoint.px_ - bottomPoint.px_);
323         if (vertical && toTop) {
324             LOG_I("turn page vertical from %{public}d to %{public}d", topPoint.py_, bottomPoint.py_);
325         } else if (vertical) {
326             LOG_I("turn page vertical from %{public}d to %{public}d", bottomPoint.py_, topPoint.py_);
327         } else if (toTop) {
328             LOG_I("turn page horizontal from %{public}d to %{public}d", topPoint.px_, bottomPoint.px_);
329         } else {
330             LOG_I("turn page horizontal from %{public}d to %{public}d", bottomPoint.px_, topPoint.px_);
331         }
332     }
333 } // namespace OHOS::uitest
334