1 /*
2  * Copyright (c) 2022-2024 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 "list_test_ng.h"
17 
18 #include "test/mock/base/mock_task_executor.h"
19 #include "test/mock/core/common/mock_theme_manager.h"
20 #include "test/mock/core/pipeline/mock_pipeline_context.h"
21 
22 #include "core/components/button/button_theme.h"
23 #include "core/components/list/list_theme.h"
24 #include "core/components_ng/pattern/linear_layout/column_model_ng.h"
25 #include "core/components_ng/pattern/linear_layout/row_model_ng.h"
26 #include "core/components_ng/syntax/repeat_virtual_scroll_model_ng.h"
27 
28 namespace OHOS::Ace::NG {
SetUpTestSuite()29 void ListTestNg::SetUpTestSuite()
30 {
31     TestNG::SetUpTestSuite();
32     auto themeManager = AceType::MakeRefPtr<MockThemeManager>();
33     MockPipelineContext::GetCurrent()->SetThemeManager(themeManager);
34     auto buttonTheme = AceType::MakeRefPtr<ButtonTheme>();
35     EXPECT_CALL(*themeManager, GetTheme(_)).WillRepeatedly(Return(buttonTheme));
36     auto listThemeConstants = CreateThemeConstants(THEME_PATTERN_LIST);
37     auto listTheme = ListTheme::Builder().Build(listThemeConstants);
38     EXPECT_CALL(*themeManager, GetTheme(ListTheme::TypeId())).WillRepeatedly(Return(listTheme));
39     auto listItemThemeConstants = CreateThemeConstants(THEME_PATTERN_LIST_ITEM);
40     auto listItemTheme = ListItemTheme::Builder().Build(listItemThemeConstants);
41     EXPECT_CALL(*themeManager, GetTheme(ListItemTheme::TypeId())).WillRepeatedly(Return(listItemTheme));
42     listItemTheme->itemDefaultColor_ = ITEMDEFAULT_COLOR;
43     listItemTheme->hoverColor_ = HOVER_COLOR;
44     listItemTheme->pressColor_ = PRESS_COLOR;
45     int32_t hoverAnimationDuration = 250;
46     int32_t hoverToPressAnimationDuration = 100;
47     listItemTheme->hoverAnimationDuration_ = hoverAnimationDuration;
48     listItemTheme->hoverToPressAnimationDuration_ = hoverToPressAnimationDuration;
49     listItemTheme->disabledAlpha_ = DISABLED_ALPHA;
50     listItemTheme->defaultColor_ = Color::WHITE;
51     listItemTheme->defaultLeftMargin_ = GROUP_MARGIN;
52     listItemTheme->defaultRightMargin_ = GROUP_MARGIN;
53     listItemTheme->defaultPadding_ = Edge(0.0_vp);
54     MockPipelineContext::GetCurrentContext()->taskExecutor_ = AceType::MakeRefPtr<MockTaskExecutor>();
55     MockAnimationManager::Enable(true);
56 }
57 
TearDownTestSuite()58 void ListTestNg::TearDownTestSuite()
59 {
60     TestNG::TearDownTestSuite();
61 }
62 
SetUp()63 void ListTestNg::SetUp()
64 {
65     MockAnimationManager::GetInstance().Reset();
66 }
67 
TearDown()68 void ListTestNg::TearDown()
69 {
70     frameNode_ = nullptr;
71     pattern_ = nullptr;
72     eventHub_ = nullptr;
73     layoutProperty_ = nullptr;
74     paintProperty_ = nullptr;
75     accessibilityProperty_ = nullptr;
76     ClearOldNodes();  // Each testcase will create new list at begin
77     AceApplicationInfo::GetInstance().isRightToLeft_ = false;
78 }
79 
GetList()80 void ListTestNg::GetList()
81 {
82     RefPtr<UINode> element = ViewStackProcessor::GetInstance()->GetMainElementNode();
83     frameNode_ = AceType::DynamicCast<FrameNode>(element);
84     pattern_ = frameNode_->GetPattern<ListPattern>();
85     eventHub_ = frameNode_->GetEventHub<ListEventHub>();
86     layoutProperty_ = frameNode_->GetLayoutProperty<ListLayoutProperty>();
87     paintProperty_ = frameNode_->GetPaintProperty<ScrollablePaintProperty>();
88     accessibilityProperty_ = frameNode_->GetAccessibilityProperty<ListAccessibilityProperty>();
89 }
90 
CreateList()91 ListModelNG ListTestNg::CreateList()
92 {
93     ResetElmtId();
94     ViewStackProcessor::GetInstance()->StartGetAccessRecordingFor(GetElmtId());
95     ListModelNG model;
96     model.Create();
97     ViewAbstract::SetWidth(CalcLength(LIST_WIDTH));
98     ViewAbstract::SetHeight(CalcLength(LIST_HEIGHT));
99     RefPtr<ScrollControllerBase> scrollController = model.CreateScrollController();
100     RefPtr<ScrollProxy> proxy = AceType::MakeRefPtr<NG::ScrollBarProxy>();
101     model.SetScroller(scrollController, proxy);
102     GetList();
103     return model;
104 }
105 
CreateListItems(int32_t itemNumber,V2::ListItemStyle listItemStyle)106 void ListTestNg::CreateListItems(int32_t itemNumber, V2::ListItemStyle listItemStyle)
107 {
108     for (int32_t index = 0; index < itemNumber; index++) {
109         CreateListItem(listItemStyle);
110         ViewStackProcessor::GetInstance()->Pop();
111         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
112     }
113 }
114 
CreateListItem(V2::ListItemStyle listItemStyle)115 ListItemModelNG ListTestNg::CreateListItem(V2::ListItemStyle listItemStyle)
116 {
117     ViewStackProcessor::GetInstance()->StartGetAccessRecordingFor(GetElmtId());
118     ListItemModelNG itemModel;
119     itemModel.Create([](int32_t) {}, listItemStyle);
120     if (layoutProperty_->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL) {
121         ViewAbstract::SetWidth(CalcLength(FILL_LENGTH));
122         ViewAbstract::SetHeight(CalcLength(ITEM_HEIGHT));
123     } else {
124         ViewAbstract::SetWidth(CalcLength(ITEM_WIDTH));
125         ViewAbstract::SetHeight(CalcLength(FILL_LENGTH));
126     }
127     return itemModel;
128 }
129 
CreateListItemGroups(int32_t groupNumber,V2::ListItemGroupStyle listItemGroupStyle)130 void ListTestNg::CreateListItemGroups(int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle)
131 {
132     for (int32_t index = 0; index < groupNumber; index++) {
133         CreateListItemGroup(listItemGroupStyle);
134         CreateListItems(GROUP_ITEM_NUMBER, static_cast<V2::ListItemStyle>(listItemGroupStyle));
135         ViewStackProcessor::GetInstance()->Pop();
136         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
137     }
138 }
139 
CreateListItemGroup(V2::ListItemGroupStyle listItemGroupStyle)140 ListItemGroupModelNG ListTestNg::CreateListItemGroup(V2::ListItemGroupStyle listItemGroupStyle)
141 {
142     ViewStackProcessor::GetInstance()->StartGetAccessRecordingFor(GetElmtId());
143     ListItemGroupModelNG groupModel;
144     groupModel.Create(listItemGroupStyle);
145     return groupModel;
146 }
147 
CreateItemWithSize(int32_t itemNumber,SizeT<Dimension> itemSize)148 void ListTestNg::CreateItemWithSize(int32_t itemNumber, SizeT<Dimension> itemSize)
149 {
150     for (int32_t index = 0; index < itemNumber; ++index) {
151         ListItemModelNG itemModel = CreateListItem();
152         ViewAbstract::SetWidth(CalcLength(itemSize.Width()));
153         ViewAbstract::SetHeight(CalcLength(itemSize.Height()));
154         ViewStackProcessor::GetInstance()->Pop();
155         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
156     }
157 }
158 
CreateGroupWithSetting(int32_t groupNumber,V2::ListItemGroupStyle listItemGroupStyle,int32_t itemNumber)159 void ListTestNg::CreateGroupWithSetting(
160     int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber)
161 {
162     for (int32_t index = 0; index < groupNumber; index++) {
163         auto header = GetRowOrColBuilder(FILL_LENGTH, Dimension(GROUP_HEADER_LEN));
164         auto footer = GetRowOrColBuilder(FILL_LENGTH, Dimension(GROUP_HEADER_LEN));
165         ListItemGroupModelNG groupModel = CreateListItemGroup(listItemGroupStyle);
166         groupModel.SetSpace(Dimension(SPACE));
167         groupModel.SetDivider(ITEM_DIVIDER);
168         groupModel.SetHeader(std::move(header));
169         groupModel.SetFooter(std::move(footer));
170         CreateListItems(itemNumber, static_cast<V2::ListItemStyle>(listItemGroupStyle));
171         ViewStackProcessor::GetInstance()->Pop();
172         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
173     }
174 }
175 
CreateGroupWithSettingChildrenMainSize(int32_t groupNumber)176 void ListTestNg::CreateGroupWithSettingChildrenMainSize(int32_t groupNumber)
177 {
178     for (int32_t index = 0; index < groupNumber; ++index) {
179         auto header = GetRowOrColBuilder(FILL_LENGTH, Dimension(GROUP_HEADER_LEN));
180         auto footer = GetRowOrColBuilder(FILL_LENGTH, Dimension(GROUP_HEADER_LEN));
181         ListItemGroupModelNG groupModel = CreateListItemGroup();
182         groupModel.SetSpace(Dimension(SPACE));
183         groupModel.SetDivider(ITEM_DIVIDER);
184         groupModel.SetHeader(std::move(header));
185         groupModel.SetFooter(std::move(footer));
186 
187         auto childrenSize = groupModel.GetOrCreateListChildrenMainSize();
188         childrenSize->UpdateDefaultSize(ITEM_HEIGHT);
189         const int32_t itemNumber = 2;
190         childrenSize->ChangeData(1, itemNumber, { 50.f, 200.f });
191         CreateListItems(1);
192         CreateItemWithSize(1, SizeT<Dimension>(FILL_LENGTH, Dimension(50.f)));
193         CreateItemWithSize(1, SizeT<Dimension>(FILL_LENGTH, Dimension(200.f)));
194         CreateListItems(1);
195         ViewStackProcessor::GetInstance()->Pop();
196         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
197     }
198 }
199 
CreateGroupChildrenMainSize(int32_t groupNumber)200 void ListTestNg::CreateGroupChildrenMainSize(int32_t groupNumber)
201 {
202     for (int32_t index = 0; index < groupNumber; index++) {
203         ListItemGroupModelNG groupModel = CreateListItemGroup();
204         auto childrenSize = groupModel.GetOrCreateListChildrenMainSize();
205         childrenSize->UpdateDefaultSize(ITEM_HEIGHT);
206         const int32_t itemNumber = 2;
207         childrenSize->ChangeData(1, itemNumber, { 50.f, 200.f });
208         CreateListItems(1);
209         CreateItemWithSize(1, SizeT<Dimension>(FILL_LENGTH, Dimension(50.f)));
210         CreateItemWithSize(1, SizeT<Dimension>(FILL_LENGTH, Dimension(200.f)));
211         CreateListItems(1);
212         ViewStackProcessor::GetInstance()->Pop();
213         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
214     }
215 }
216 
CreateGroupWithItem(int32_t groupNumber,Axis axis)217 void ListTestNg::CreateGroupWithItem(int32_t groupNumber, Axis axis)
218 {
219     for (int32_t index = 0; index < groupNumber; index++) {
220         if (index & 1) {
221             CreateListItems(1);
222         } else {
223             CreateListItemGroups(1);
224         }
225     }
226 }
227 
CreateSwipeItems(std::function<void ()> startAction,std::function<void ()> endAction,V2::SwipeEdgeEffect effect,int32_t itemNumber)228 void ListTestNg::CreateSwipeItems(
229     std::function<void()> startAction, std::function<void()> endAction,
230     V2::SwipeEdgeEffect effect, int32_t itemNumber)
231 {
232     for (int32_t index = 0; index < itemNumber; index++) {
233         ListItemModelNG itemModel = CreateListItem();
234         itemModel.SetSwiperAction(nullptr, nullptr, nullptr, effect);
235         if (startAction) {
236             itemModel.SetDeleteArea(
237                 std::move(startAction), nullptr, nullptr, nullptr, nullptr, Dimension(DELETE_AREA_DISTANCE), true);
238         }
239         if (endAction) {
240             itemModel.SetDeleteArea(
241                 std::move(endAction), nullptr, nullptr, nullptr, nullptr, Dimension(DELETE_AREA_DISTANCE), false);
242         }
243         {
244             Axis axis = layoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
245             float mainSize = axis == Axis::VERTICAL ? ITEM_HEIGHT : ITEM_WIDTH;
246             GetRowOrColBuilder(FILL_LENGTH, Dimension(mainSize))();
247             ViewStackProcessor::GetInstance()->Pop();
248         }
249         ViewStackProcessor::GetInstance()->Pop();
250         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
251     }
252 }
253 
GetRowOrColBuilder(float crossSize,float mainSize)254 std::function<void()> ListTestNg::GetRowOrColBuilder(float crossSize, float mainSize)
255 {
256     return GetRowOrColBuilder(Dimension(crossSize), Dimension(mainSize));
257 }
258 
GetRowOrColBuilder(Dimension crossSize,Dimension mainSize)259 std::function<void()> ListTestNg::GetRowOrColBuilder(Dimension crossSize, Dimension mainSize)
260 {
261     Axis axis = layoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
262     return [axis, mainSize, crossSize]() {
263         if (axis == Axis::VERTICAL) {
264             RowModelNG rowModel;
265             rowModel.Create(std::nullopt, nullptr, "");
266             ViewAbstract::SetWidth(CalcLength(crossSize));
267             ViewAbstract::SetHeight(CalcLength(mainSize));
268         } else {
269             ColumnModelNG colModel;
270             colModel.Create(Dimension(0), nullptr, "");
271             ViewAbstract::SetWidth(CalcLength(mainSize));
272             ViewAbstract::SetHeight(CalcLength(crossSize));
273         }
274     };
275 }
276 
UpdateCurrentOffset(float offset,int32_t source)277 void ListTestNg::UpdateCurrentOffset(float offset, int32_t source)
278 {
279     pattern_->UpdateCurrentOffset(offset, source);
280     FlushLayoutTask(frameNode_);
281 }
282 
ScrollToEdge(ScrollEdgeType scrollEdgeType)283 void ListTestNg::ScrollToEdge(ScrollEdgeType scrollEdgeType)
284 {
285     pattern_->ScrollToEdge(scrollEdgeType, false);
286     FlushLayoutTask(frameNode_);
287 }
288 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)289 void ListTestNg::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
290 {
291     pattern_->ScrollToIndex(index, smooth, align, extraOffset);
292     FlushLayoutTask(frameNode_);
293     if (smooth) {
294         auto iter = pattern_->itemPosition_.find(index);
295         float targetPos = 0.0f;
296         if (iter->second.isGroup) {
297             if (!pattern_->GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos,
298                 iter->second.endPos, align, targetPos)) {
299                 return;
300             }
301         } else {
302             pattern_->GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
303         }
304         if (extraOffset.has_value()) {
305             targetPos += extraOffset.value();
306         }
307         if (!NearZero(targetPos)) {
308             float endValue = pattern_->GetFinalPosition();
309             pattern_->ScrollTo(endValue);
310             FlushLayoutTask(frameNode_);
311         }
312     }
313 }
314 
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)315 void ListTestNg::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
316 {
317     pattern_->ScrollToItemInGroup(index, indexInGroup, smooth, align);
318     FlushLayoutTask(frameNode_);
319     if (smooth) {
320         float endValue = pattern_->GetFinalPosition();
321         pattern_->ScrollTo(endValue);
322         FlushLayoutTask(frameNode_);
323     }
324 }
325 
DragSwiperItem(int32_t index,float mainDelta,float mainVelocity)326 void ListTestNg::DragSwiperItem(int32_t index, float mainDelta, float mainVelocity)
327 {
328     HandleDragStart(index);
329     HandleDragUpdate(index, mainDelta);
330     HandleDragEnd(index, mainVelocity);
331 }
332 
HandleDragStart(int32_t index)333 void ListTestNg::HandleDragStart(int32_t index)
334 {
335     GestureEvent info;
336     auto itemPattern = GetChildPattern<ListItemPattern>(frameNode_, index);
337     auto handleDragStart = itemPattern->panEvent_->GetActionStartEventFunc();
338     handleDragStart(info);
339 }
340 
HandleDragUpdate(int32_t index,float mainDelta)341 void ListTestNg::HandleDragUpdate(int32_t index, float mainDelta)
342 {
343     GestureEvent info;
344     info.SetMainDelta(mainDelta);
345     auto itemPattern = GetChildPattern<ListItemPattern>(frameNode_, index);
346     auto handleDragUpdate = itemPattern->panEvent_->GetActionUpdateEventFunc();
347     handleDragUpdate(info);
348     frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
349     FlushLayoutTask(frameNode_);
350 }
351 
HandleDragEnd(int32_t index,float mainVelocity)352 void ListTestNg::HandleDragEnd(int32_t index, float mainVelocity)
353 {
354     GestureEvent info;
355     info.SetMainVelocity(mainVelocity);
356     auto itemPattern = GetChildPattern<ListItemPattern>(frameNode_, index);
357     auto handleDragEnd = itemPattern->panEvent_->GetActionEndEventFunc();
358     handleDragEnd(info);
359     // curOffset_ would be NodeSize or Zero
360     EXPECT_NE(itemPattern->springMotion_, nullptr);
361     double position = itemPattern->springMotion_->GetEndValue();
362     itemPattern->UpdatePostion(position - itemPattern->curOffset_);
363     frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
364     FlushLayoutTask(frameNode_);
365 }
366 
ScrollSnap(double offset,double endVelocity)367 void ListTestNg::ScrollSnap(double offset, double endVelocity)
368 {
369     double velocity = offset > 0.f ? 1200.f : -1200.f;
370     // Define (150.0, 500.0) as finger press position.
371     double touchPosX = 150.0;
372     double touchPosY = 500.0;
373     // Generate pan gesture from finger for List sliding.
374     GestureEvent info;
375     info.SetMainVelocity(velocity);
376     info.SetGlobalPoint(Point(touchPosX, touchPosY));
377     info.SetGlobalLocation(Offset(touchPosX, touchPosY));
378     info.SetSourceTool(SourceTool::FINGER);
379     info.SetInputEventType(InputEventType::TOUCH_SCREEN);
380     // Call HandleTouchDown and HandleDragStart.
381     auto scrollable = pattern_->scrollableEvent_->GetScrollable();
382     scrollable->HandleTouchDown();
383     scrollable->isDragging_ = true;
384     scrollable->HandleDragStart(info);
385 
386     // Update finger position.
387     info.SetGlobalLocation(Offset(touchPosX, touchPosY + offset));
388     info.SetGlobalPoint(Point(touchPosX, touchPosY + offset));
389     info.SetMainVelocity(velocity);
390     info.SetMainDelta(offset);
391     scrollable->HandleDragUpdate(info);
392     FlushLayoutTask(frameNode_);
393 
394     // Lift finger and end List sliding.
395     info.SetMainVelocity(endVelocity);
396     info.SetMainDelta(0.0);
397     scrollable->HandleTouchUp();
398     scrollable->HandleDragEnd(info);
399     scrollable->isDragging_ = false;
400     FlushLayoutTask(frameNode_);
401 
402     if (scrollable->IsSpringMotionRunning()) {
403         // If current position is out of boundary, trig spring motion.
404         float endValue = scrollable->GetFinalPosition();
405         scrollable->ProcessSpringMotion(endValue);
406         scrollable->StopSpringAnimation();
407         FlushLayoutTask(frameNode_);
408     } else if (!(scrollable->isSnapScrollAnimationStop_)) {
409         // StartScrollSnapMotion, for condition that equal item height.
410         float endValue = scrollable->GetSnapFinalPosition();
411         scrollable->ProcessScrollSnapMotion(endValue);
412         scrollable->ProcessScrollSnapStop();
413         FlushLayoutTask(frameNode_);
414     }
415     scrollable->StopScrollable();
416 }
417 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,float expectOffset)418 AssertionResult ListTestNg::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, float expectOffset)
419 {
420     // After every call to ScrollToIndex(), reset currentDelta_
421     float startOffset = pattern_->GetTotalOffset();
422     pattern_->ScrollToIndex(index, smooth, align);
423     FlushLayoutTask(frameNode_);
424     if (smooth) {
425         // Because can not get targetPos, use source code
426         auto iter = pattern_->itemPosition_.find(index);
427         float targetPos = 0.0f;
428         if (iter->second.isGroup) {
429             pattern_->GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos,
430                 iter->second.endPos, align, targetPos);
431         } else {
432             pattern_->GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
433         }
434         if (!NearZero(targetPos)) {
435             // Straight to the end of the anmiation, use ScrollTo replace AnimateTo
436             float finalPosition = pattern_->GetFinalPosition();
437             float totalHeight = pattern_->GetTotalHeight();
438             finalPosition = std::clamp(finalPosition, 0.f, totalHeight); // limit scrollDistance
439             pattern_->ScrollTo(finalPosition);
440             FlushLayoutTask(frameNode_);
441         }
442     }
443     float currentOffset = pattern_->GetTotalOffset();
444     pattern_->ScrollTo(startOffset); // reset offset before return
445     FlushLayoutTask(frameNode_);
446     return IsEqual(currentOffset, expectOffset);
447 }
448 
JumpToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align,float expectOffset)449 AssertionResult ListTestNg::JumpToItemInGroup(
450     int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align, float expectOffset)
451 {
452     auto controller = pattern_->positionController_;
453     float startOffset = pattern_->GetTotalOffset();
454     controller->JumpToItemInGroup(index, indexInGroup, smooth, align);
455     FlushLayoutTask(frameNode_);
456     if (smooth) {
457         // Because can not get targetPos, use source code
458         auto iter = pattern_->itemPosition_.find(index);
459         float targetPos = 0.0f;
460         if (iter->second.isGroup) {
461             pattern_->GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup,
462                 iter->second.startPos, align, targetPos);
463         } else {
464             pattern_->GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
465         }
466         if (!NearZero(targetPos)) {
467             // Straight to the end of the anmiation, use ScrollTo replace AnimateTo
468             float finalPosition = pattern_->GetFinalPosition();
469             float totalHeight = pattern_->GetTotalHeight();
470             finalPosition = std::clamp(finalPosition, 0.f, totalHeight); // limit scrollDistance
471             pattern_->ScrollTo(finalPosition);
472             FlushLayoutTask(frameNode_);
473         }
474     }
475     float currentOffset = pattern_->GetTotalOffset();
476     pattern_->ScrollTo(startOffset); // reset offset before return
477     FlushLayoutTask(frameNode_);
478     return IsEqual(currentOffset, expectOffset);
479 }
480 
481 // Get all listItem that in or not in listItemGroup
GetALLItem()482 std::vector<RefPtr<FrameNode>> ListTestNg::GetALLItem()
483 {
484     std::vector<RefPtr<FrameNode>> listItems;
485     auto children = frameNode_->GetChildren();
486     for (auto child : children) {
487         auto childFrameNode = AceType::DynamicCast<FrameNode>(child);
488         if (childFrameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
489             auto group = child->GetChildren();
490             for (auto item : group) {
491                 auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
492                 if (itemFrameNode->GetTag() == V2::LIST_ITEM_ETS_TAG) {
493                     listItems.emplace_back(itemFrameNode);
494                 }
495             }
496         } else if (childFrameNode->GetTag() == V2::LIST_ITEM_ETS_TAG) {
497             listItems.emplace_back(childFrameNode);
498         }
499     }
500     return listItems;
501 }
502 
findFocusNodeIndex(RefPtr<FocusHub> & focusNode)503 int32_t ListTestNg::findFocusNodeIndex(RefPtr<FocusHub>& focusNode)
504 {
505     std::vector<RefPtr<FrameNode>> listItems = GetALLItem();
506     int32_t size = static_cast<int32_t>(listItems.size());
507     for (int32_t index = 0; index < size; index++) {
508         if (focusNode == listItems[index]->GetOrCreateFocusHub()) {
509             return index;
510         }
511     }
512     return NULL_VALUE;
513 }
514 
ScrollTo(float position)515 void ListTestNg::ScrollTo(float position)
516 {
517     pattern_->ScrollTo(position);
518     FlushLayoutTask(frameNode_);
519 }
520 
CreateRepeatVirtualScrollNode(int32_t itemNumber,const std::function<void (uint32_t)> & createFunc)521 void ListTestNg::CreateRepeatVirtualScrollNode(int32_t itemNumber, const std::function<void(uint32_t)>& createFunc)
522 {
523     RepeatVirtualScrollModelNG repeatModel;
524     std::function<void(const std::string&, uint32_t)> updateFunc = [](const std::string& value, uint32_t idx) {};
525     std::function<std::list<std::string>(uint32_t, uint32_t)> getKeys = [](uint32_t start, uint32_t end) {
526         std::list<std::string> keys;
527         for (uint32_t i = start; i <= end; ++i) {
528             keys.emplace_back(std::to_string(i));
529         }
530         return keys;
531     };
532     std::function<std::list<std::string>(uint32_t, uint32_t)> getTypes = [](uint32_t start, uint32_t end) {
533         std::list<std::string> keys;
534         for (uint32_t i = start; i <= end; ++i) {
535             keys.emplace_back("0");
536         }
537         return keys;
538     };
539     std::function<void(uint32_t, uint32_t)> setActiveRange = [](uint32_t start, uint32_t end) {};
540     repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes, setActiveRange);
541 }
542 
FlushIdleTask(const RefPtr<ListPattern> & listPattern)543 void ListTestNg::FlushIdleTask(const RefPtr<ListPattern>& listPattern)
544 {
545     int32_t tryCount = 10;
546     auto predictParam = listPattern->GetPredictLayoutParamV2();
547     while (predictParam && tryCount > 0) {
548         const int64_t time = GetSysTimestamp();
549         PipelineContext::GetCurrentContext()->OnIdle(time + 16 * 1000000); // 16 * 1000000: 16ms
550         FlushLayoutTask(frameNode_);
551         predictParam = listPattern->GetPredictLayoutParamV2();
552         tryCount--;
553     }
554 }
555 
CreateGroupWithSettingWithComponentContent(int32_t groupNumber,V2::ListItemGroupStyle listItemGroupStyle,int32_t itemNumber)556 void ListTestNg::CreateGroupWithSettingWithComponentContent(
557     int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber)
558 {
559     for (int32_t index = 0; index < groupNumber; index++) {
560         ListItemGroupModelNG groupModel = CreateListItemGroup(listItemGroupStyle);
561         groupModel.SetSpace(Dimension(SPACE));
562         groupModel.SetDivider(ITEM_DIVIDER);
563         groupModel.SetHeaderComponent(CreateCustomNode("Header"));
564         groupModel.SetFooterComponent(CreateCustomNode("Footer"));
565         CreateListItems(itemNumber, static_cast<V2::ListItemStyle>(listItemGroupStyle));
566         ViewStackProcessor::GetInstance()->Pop();
567         ViewStackProcessor::GetInstance()->StopGetAccessRecording();
568     }
569 }
570 
CreateCustomNode(const std::string & tag)571 RefPtr<FrameNode> ListTestNg::CreateCustomNode(const std::string& tag)
572 {
573     auto frameNode = AceType::MakeRefPtr<FrameNode>(
574         tag, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<Pattern>());
575     auto layoutProperty = frameNode->GetLayoutProperty();
576     layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(LIST_WIDTH), CalcLength(LIST_HEIGHT)));
577     return frameNode;
578 }
579 } // namespace OHOS::Ace::NG
580