From 8f1d8a0dd6b6305b23479d786c4a09565cf53370 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Tue, 19 Mar 2024 23:11:01 +0800
Subject: [PATCH 01/21] fix: fix assert errors for position layout.

---
 webf/lib/src/dom/document.dart                     | 10 +++++-----
 webf/lib/src/dom/element.dart                      |  4 ++--
 webf/lib/src/rendering/box_model.dart              |  4 ++++
 webf/lib/src/rendering/flex.dart                   | 12 ++++++------
 webf/lib/src/rendering/flow.dart                   |  4 +---
 webf/lib/src/rendering/paragraph.dart              |  2 +-
 webf/lib/src/rendering/render_position_holder.dart |  4 +++-
 webf/lib/src/rendering/replaced.dart               | 12 +++++-------
 webf/lib/src/rendering/sliver_list.dart            |  2 +-
 webf/lib/src/rendering/text.dart                   |  4 +---
 webf/lib/src/rendering/widget.dart                 | 12 +++++-------
 11 files changed, 34 insertions(+), 36 deletions(-)

diff --git a/webf/lib/src/dom/document.dart b/webf/lib/src/dom/document.dart
index 601016bdee..46268fa8bd 100644
--- a/webf/lib/src/dom/document.dart
+++ b/webf/lib/src/dom/document.dart
@@ -448,27 +448,27 @@ class Document extends ContainerNode {
     return super.replaceChild(newNode, oldNode);
   }
 
-  Element createElement(String type, [BindingContext? context]) {
+  Element createElement(String type, BindingContext context) {
     Element element = element_registry.createElement(type, context);
     return element;
   }
 
-  Element createElementNS(String uri, String type, [BindingContext? context]) {
+  Element createElementNS(String uri, String type, BindingContext context) {
     Element element = element_registry.createElementNS(uri, type, context);
     return element;
   }
 
-  TextNode createTextNode(String data, [BindingContext? context]) {
+  TextNode createTextNode(String data, BindingContext context) {
     TextNode textNode = TextNode(data, context);
     return textNode;
   }
 
-  DocumentFragment createDocumentFragment([BindingContext? context]) {
+  DocumentFragment createDocumentFragment(BindingContext context) {
     DocumentFragment documentFragment = DocumentFragment(context);
     return documentFragment;
   }
 
-  Comment createComment([BindingContext? context]) {
+  Comment createComment(BindingContext context) {
     Comment comment = Comment(context);
     return comment;
   }
diff --git a/webf/lib/src/dom/element.dart b/webf/lib/src/dom/element.dart
index a18941da65..70066f8221 100644
--- a/webf/lib/src/dom/element.dart
+++ b/webf/lib/src/dom/element.dart
@@ -869,7 +869,7 @@ abstract class Element extends ContainerNode with ElementBase, ElementEventMixin
       if (previousPseudoElement.firstChild != null) {
         (previousPseudoElement.firstChild as TextNode).data = pseudoValue.value;
       } else {
-        final textNode = ownerDocument.createTextNode(pseudoValue.value);
+        final textNode = ownerDocument.createTextNode(pseudoValue.value, BindingContext(ownerDocument.controller.view, contextId!, allocateNewBindingObject()));
         previousPseudoElement.appendChild(textNode);
       }
     }
@@ -1772,7 +1772,7 @@ abstract class Element extends ContainerNode with ElementBase, ElementEventMixin
 
   @override
   String toString() {
-    return '$tagName Element($hashCode)';
+    return '$tagName Element($hashCode) $className';
   }
 
   // Create a new RenderLayoutBox for the scrolling content.
diff --git a/webf/lib/src/rendering/box_model.dart b/webf/lib/src/rendering/box_model.dart
index a4c89029b6..ac0694feb3 100644
--- a/webf/lib/src/rendering/box_model.dart
+++ b/webf/lib/src/rendering/box_model.dart
@@ -1531,10 +1531,14 @@ class RenderBoxModel extends RenderBox
     }
   }
 
+  bool _disposed = false;
+  bool get disposed => _disposed;
+
   /// Called when its corresponding element disposed
   @override
   @mustCallSuper
   void dispose() {
+    _disposed = true;
     super.dispose();
 
     // Dispose scroll behavior
diff --git a/webf/lib/src/rendering/flex.dart b/webf/lib/src/rendering/flex.dart
index 6b6490a94b..24bcaa0dd2 100644
--- a/webf/lib/src/rendering/flex.dart
+++ b/webf/lib/src/rendering/flex.dart
@@ -169,9 +169,7 @@ class RenderFlexLayout extends RenderLayoutBox {
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! RenderLayoutParentData) {
-      child.parentData = RenderLayoutParentData();
-    }
+    child.parentData = RenderLayoutParentData();
     if (child is RenderBoxModel) {
       child.parentData = CSSPositionedLayout.getPositionParentData(child, child.parentData as RenderLayoutParentData);
     }
@@ -2364,9 +2362,11 @@ class RenderFlexLayout extends RenderLayoutBox {
   static bool _isPlaceholderPositioned(RenderObject child) {
     if (child is RenderPositionPlaceholder) {
       RenderBoxModel realDisplayedBox = child.positioned!;
-      RenderLayoutParentData parentData = realDisplayedBox.parentData as RenderLayoutParentData;
-      if (parentData.isPositioned) {
-        return true;
+      if (realDisplayedBox.attached) {
+        RenderLayoutParentData? parentData = realDisplayedBox.parentData as RenderLayoutParentData?;
+        if (parentData?.isPositioned == true) {
+          return true;
+        }
       }
     }
     return false;
diff --git a/webf/lib/src/rendering/flow.dart b/webf/lib/src/rendering/flow.dart
index 17b2f9ea82..1627f8d4f2 100644
--- a/webf/lib/src/rendering/flow.dart
+++ b/webf/lib/src/rendering/flow.dart
@@ -67,9 +67,7 @@ class RenderFlowLayout extends RenderLayoutBox {
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! RenderLayoutParentData) {
-      child.parentData = RenderLayoutParentData();
-    }
+    child.parentData = RenderLayoutParentData();
     if (child is RenderBoxModel) {
       child.parentData = CSSPositionedLayout.getPositionParentData(child, child.parentData as RenderLayoutParentData);
     }
diff --git a/webf/lib/src/rendering/paragraph.dart b/webf/lib/src/rendering/paragraph.dart
index 060ac57d3d..e09c9d68be 100644
--- a/webf/lib/src/rendering/paragraph.dart
+++ b/webf/lib/src/rendering/paragraph.dart
@@ -61,7 +61,7 @@ class WebFRenderParagraph extends RenderBox
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! TextParentData) child.parentData = TextParentData();
+    child.parentData = TextParentData();
   }
 
   final TextPainter _textPainter;
diff --git a/webf/lib/src/rendering/render_position_holder.dart b/webf/lib/src/rendering/render_position_holder.dart
index f71fe1bd71..8e3ffcf9e4 100644
--- a/webf/lib/src/rendering/render_position_holder.dart
+++ b/webf/lib/src/rendering/render_position_holder.dart
@@ -36,7 +36,9 @@ class RenderPositionPlaceholder extends RenderPreferredSize {
     // The relative offset of positioned renderBox are depends on positionHolder' offset.
     // When the placeHolder got layout, should notify the positioned renderBox to layout again.
     SchedulerBinding.instance.scheduleFrameCallback((_) {
-      positioned?.markNeedsLayout();
+      if (positioned?.disposed == false) {
+        positioned?.markNeedsLayout();
+      }
     });
   }
 
diff --git a/webf/lib/src/rendering/replaced.dart b/webf/lib/src/rendering/replaced.dart
index 83a08d8f2a..15c0917cb1 100644
--- a/webf/lib/src/rendering/replaced.dart
+++ b/webf/lib/src/rendering/replaced.dart
@@ -41,13 +41,11 @@ class RenderReplaced extends RenderBoxModel with RenderObjectWithChildMixin<Rend
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! RenderLayoutParentData) {
-      if (child is RenderBoxModel) {
-        RenderLayoutParentData parentData = RenderLayoutParentData();
-        child.parentData = CSSPositionedLayout.getPositionParentData(child, parentData);
-      } else {
-        child.parentData = RenderLayoutParentData();
-      }
+    if (child is RenderBoxModel) {
+      RenderLayoutParentData parentData = RenderLayoutParentData();
+      child.parentData = CSSPositionedLayout.getPositionParentData(child, parentData);
+    } else {
+      child.parentData = RenderLayoutParentData();
     }
   }
 
diff --git a/webf/lib/src/rendering/sliver_list.dart b/webf/lib/src/rendering/sliver_list.dart
index 1230e1af7c..9292adbbca 100644
--- a/webf/lib/src/rendering/sliver_list.dart
+++ b/webf/lib/src/rendering/sliver_list.dart
@@ -116,7 +116,7 @@ class RenderSliverListLayout extends RenderLayoutBox {
   void setupParentData(RenderBox child) {
     if (child == _renderViewport && child.parentData is! RenderLayoutParentData) {
       child.parentData = RenderLayoutParentData();
-    } else if (child.parentData is! SliverMultiBoxAdaptorParentData) {
+    } else {
       child.parentData = SliverMultiBoxAdaptorParentData();
     }
   }
diff --git a/webf/lib/src/rendering/text.dart b/webf/lib/src/rendering/text.dart
index 6402f422f5..138ea9ec80 100644
--- a/webf/lib/src/rendering/text.dart
+++ b/webf/lib/src/rendering/text.dart
@@ -139,9 +139,7 @@ class RenderTextBox extends RenderBox with RenderObjectWithChildMixin<RenderBox>
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! TextParentData) {
-      child.parentData = TextParentData();
-    }
+    child.parentData = TextParentData();
   }
 
   int? get _maxLines {
diff --git a/webf/lib/src/rendering/widget.dart b/webf/lib/src/rendering/widget.dart
index 272e499c86..e993d02cac 100644
--- a/webf/lib/src/rendering/widget.dart
+++ b/webf/lib/src/rendering/widget.dart
@@ -26,13 +26,11 @@ class RenderWidget extends RenderBoxModel with RenderObjectWithChildMixin<Render
 
   @override
   void setupParentData(RenderBox child) {
-    if (child.parentData is! RenderLayoutParentData) {
-      if (child is RenderBoxModel) {
-        RenderLayoutParentData parentData = RenderLayoutParentData();
-        child.parentData = CSSPositionedLayout.getPositionParentData(child, parentData);
-      } else {
-        child.parentData = RenderLayoutParentData();
-      }
+    if (child is RenderBoxModel) {
+      RenderLayoutParentData parentData = RenderLayoutParentData();
+      child.parentData = CSSPositionedLayout.getPositionParentData(child, parentData);
+    } else {
+      child.parentData = RenderLayoutParentData();
     }
   }
 

From c261bd3f570933333e4983fbe1e76bc97d0d4eb0 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sat, 31 Aug 2024 15:43:12 +0800
Subject: [PATCH 02/21] fix: fix integration test.

---
 bridge/polyfill/src/test/index.js           | 1 +
 bridge/test/webf_test_context.cc            | 6 ++++++
 integration_tests/specs/blob/constructor.ts | 2 +-
 webf/lib/src/module/async_storage.dart      | 2 +-
 4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/bridge/polyfill/src/test/index.js b/bridge/polyfill/src/test/index.js
index 14dc088bf1..cec6332bc1 100644
--- a/bridge/polyfill/src/test/index.js
+++ b/bridge/polyfill/src/test/index.js
@@ -60,6 +60,7 @@ class JasmineTracker {
     resetDocumentElement();
     webf.methodChannel.clearMethodCallHandler();
     document.___clear_cookies__();
+    __webf_sync_buffer__();
   }
   specStarted(result) {
   }
diff --git a/bridge/test/webf_test_context.cc b/bridge/test/webf_test_context.cc
index 2a82c1238d..4c4df8227d 100644
--- a/bridge/test/webf_test_context.cc
+++ b/bridge/test/webf_test_context.cc
@@ -265,6 +265,11 @@ static JSValue parseHTML(JSContext* ctx, JSValueConst this_val, int argc, JSValu
   return JS_NULL;
 }
 
+static JSValue syncThreadBuffer(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
+  auto* context = static_cast<ExecutingContext*>(JS_GetContextOpaque(ctx));
+  context->uiCommandBuffer()->SyncToActive();
+}
+
 static JSValue triggerGlobalError(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
   auto* context = static_cast<ExecutingContext*>(JS_GetContextOpaque(ctx));
 
@@ -357,6 +362,7 @@ WebFTestContext::WebFTestContext(ExecutingContext* context)
       {"__webf_environment__", environment, 0},
       {"__webf_simulate_pointer__", simulatePointer, 3},
       {"__webf_simulate_inputtext__", simulateInputText, 1},
+      {"__webf_sync_buffer__", syncThreadBuffer, 0},
       {"__webf_trigger_global_error__", triggerGlobalError, 0},
       {"__webf_parse_html__", parseHTML, 1},
   };
diff --git a/integration_tests/specs/blob/constructor.ts b/integration_tests/specs/blob/constructor.ts
index d6bcf890bf..1945b0cf61 100644
--- a/integration_tests/specs/blob/constructor.ts
+++ b/integration_tests/specs/blob/constructor.ts
@@ -49,6 +49,6 @@ describe('Blob API', () => {
     // @ts-ignore
     const base64 = await blob.base64();
 
-    expect(base64).toEqual('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAAiCAYAAAByUUNrAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAATbSURBVHic7dx/aFZVHMfx9xPSD9uzskyzNoqJQgUWgmAJWuSyZRAaJEG/oLZW/hNKP/6o/iiiEJSIiKIkKqEysPJHZYX2w1US9YfhMimtpik5aqTLTXO3P77fy84Oj9vu2EMn+bzgcLjfc+6553n++D535567EpAhIiLJGQPK0CIiqSkBJ/3XkxARkcqUoEVEEqUELSKSKCVoEZFEKUGLiCRKCVpEJFFK0CIiiVKCFhFJVOEEvQ14f5Qu/hvwDnAwiH0IfD1K41dbLzb/3cPo+wXwSXWnIyInmMIJ+llg0ShdfDOwAEvUuTuBZ0Zp/Grbj81/0zD6PgY8UN3piMgJRkscIiKJUoIWEUnUiBP0i8Bk7B96TAQeB/qC9mPASuBS71MLLAQ6RnCtz4CrfYwScAmwAjji7Ws8tis4Z7fHro3GWufxX/x4F3ArUO9jTwYWAweCczb4ORuA2d7vr0Hm+zxwOf3fzTIGfjciIsMxogR9EFgK3IStF08BHgXWB31eAe7CEt6rwJNAG3AF8E+Ba+0A5gB7gKd83Mv8+k94n4uBdiyR5z7w2EbghyD+HvYjUY895GsE3gXuAVZh6+vPYWvL+X/56/KxrgdqgKeBsceZ7ws+VtnHWQIsBz4q8JlFRHJZVqA0W97KPg1i+zz2SBBrgGxGdO4677fZj1f58Y6gTx1ktwTHrd6nIxqryeM9kPVBNgGyO6L2Ru+zIppX3u91b18djb3M423RPFuifj97/KUgNtWv0RvE2r1f/H2oqKioHK8A2YjuoMvYn/q5c73u9roXWzqYje3QaMfunvd4+74C19qO3SHXRfEmr/diSwk3YHfHAIewrYA3AzcCb3u8w+c1z493ej0nGnuu1z9F8cVDzPVvH/M64OQgfhEwfYhzRURiY6oxaL5+u9xLrgw0AGcXGGsvMKlC/Eyv9/uYjdi6+I/A9942D/uAtwGd2I8EwFVe5z8UtdHYZ0TtuVOGmGuX12dVaJsE/D7E+SIioaok6DxBtQIPYwmwBrvTLWoC8GeF+CGv82Sf39FvAbYCM4DzgGs8vtHbpmMP7gDOCcY6NRg7/0tgfMG51kRzC3VViImIDKYq2+zGYol1O3A+duc8WHI+Fh2HDxGnYEsknVGfrV7Xez0RmIa9NLIGW9rI4zOBtdhbivODMS70On5z8RuvLxhkzqF8/rXYZ43fGDxA/927iMhwVW0f9H3A59gWtrXYroqV2JuCuXzt+k0sCYMlxS3YGvJh4G6PL8ISbBv9uzlaGLibYj7wGraU0BTEFwKrsfXnuUF8AZZQW4C3gK+wnRwPAlMZuM5eyTiv19O/g6QZ+BbbybEJ2yESb/UTERmuQk8Wm7EdE5WeON4fHB+G7CGPh+XKoM8RyGZ5vNVjb0BW9tivHnvZrxmOcztkndEcPva2uij+XXBeT9T2JWTTorFnQbYt6JPv4thZ4XMv9bYGP+7GdonkY5UhWwLZvZDNTODJsIqKyv+jAFnJE3TV9GEP8o5ia76V9g93AafR/xDuKLaOOy7q9wfQgy2fjPbiebePP97nUvRcgNODWC/2uevR65oiUlzJS1UTtIiIFFdCN3ciIslSghYRSZQStIhIopSgRUQSpQQtIpIoJWgRkUQpQYuIJEoJWkQkUSVA76mIiCToX+zyAmIAp2CbAAAAAElFTkSuQmCC');
+    expect(base64).toEqual('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAAiCAYAAAByUUNrAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAS7SURBVHic7d1/aFZVHMfx9xPSD9tWlmnWRjFRqMBCECxBi1y2DEKDJOgX1NbKf0Lpxx/VH0UUghIRUZREJVQGVv7IrNB+uEqi/jBcJqXVNCVHjXS5ae72R58Lh8Pj9tzxPHaSzwsOh/s955577vPHd3fnnmcrARlmZpacUThDm5klpwSc9F9PwszMynOCNjNLlBO0mVminKDNzBLlBG1mlignaDOzRDlBm5klygnazCxRhRP0VmB9lS7+K/AOcCCIfQB8VaXxa21A899VQd/PgY+Pw5zM7MRROEE/Cyyo0sU3AfOUqHN3As9Uafxa26f5b6yg72PAA8dhTmZ24vASh5lZopygzcwSNeIE/SIwUX/QYzzwODAYtB8FlgOXqk8DMB/oHsG1PgWu1hgl4BJgGXBY7asU2xmcs0uxa6Ox1ij+s453ArcCTRp7IrAQ2B+cs07nrANmqt+fQ8z3eeDy4LNZEn02ZmaVGFGCPgAsBm7SevEk4FFgbdDnFeAuJbxXgSeBTuAK4O8C19oOzAJ2A09p3Mt0/SfU52KgS4k8975iG4Dvg/h7+iHRpJd8LcC7wD3ACq2vP6e15fyv/PVqrOuBOuBpYPQx5vuCxqrXOIuApcCHBe7ZzCyXZQVK2795K/skiO1V7JEg1gzZtOjcNeq3SccrdLw96NMI2S3BcYf6dEdjtSreD9kgZOMguyNqb1GfZdG88n6vq31lNPYSxTujebZH/X5S/KUgNlnXGAhiXeoXfx4uLi4uxypANqIn6Hr9qp87V3Wf6gEtHczUDo0uPT3vVvveAtfapifkxijeqnqPlhJu0NMxwEFtBbwZuBF4W/FuzWuOjneonhWNPVv1j1F84TBz/UtjXgecHMQvAqZWcK9mZqFRtRg0X79dqpKrB5qBswuMtQeYUCZ+pup9GrNF6+I/AN+pbY5u8DagRz8kAK5Snf+gaIjGPiNqz50yzFx7VZ9Vpm0C8Nsw55uZhWqSoPME1QE8rARYpyfdosYBf5SJH1SdJ/v8iX4zsAWYBpwHXKP4BrVN1Ys7gHOCsU4Nxs5/ExhbcK510dxCvWViZmZDqck2u9FKrNuA8/XkPFRyPhodhy8RJ2mJpCfqs0V1k+rxwBR9aWSVljby+HRgtb6lODcY40LV8TcXv1Z9QQX3Gs6/Qfcaf2Nwf/D0bmZWqZrtg74P+Exb2FZrV8VyfVMwl69dv6kkjJLiZq0hHwLuVnyBEmxnsJujPdpNMRd4TUsJrUF8PrBS68+zg/g8JdR24C3gS+3keBCYHK2zlzNG9dpgB0kb8I12cmzUDpF4q5+ZWaUKvVls046Jcm8c7w+OD0H2kOJhuTLocxiyGYp3KPYGZPWK/aLYy7pmOM7tkPVEc/hIbY1R/NvgvP6o7QvIpkRjz4Bsa9An38Wxo8x9L1Zbs477tEskH6seskWQ3QvZ9ATeDLu4uPw/CpCVlKBrZlAv8o5ozbfc/uFe4LTgJdwRreOOifr9DvRr+aTai+d9Gn+s5lL0XIDTg9iA7rvJX9c0sxEoqdQ0QZuZWXElP9yZmaXLCdrMLFFO0GZmiXKCNjNLlBO0mVminKDNzBLlBG1mlignaDOzRJWCfxxiZmYJ+Qfs8gJi9ynfLgAAAABJRU5ErkJggg==');
   });
 });
diff --git a/webf/lib/src/module/async_storage.dart b/webf/lib/src/module/async_storage.dart
index ab295c79a9..f1519044e4 100644
--- a/webf/lib/src/module/async_storage.dart
+++ b/webf/lib/src/module/async_storage.dart
@@ -52,7 +52,7 @@ class AsyncStorageModule extends BaseModule {
 
   Future<bool> removeItem(String key) async {
     try {
-      _lazyBox.delete(key);
+      await _lazyBox.delete(key);
       return true;
     } catch (e, stack) {
       return false;

From a274edfdb01fd85af29616c23163feb7d71c9031 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sat, 31 Aug 2024 20:00:29 +0800
Subject: [PATCH 03/21] chore: use flutter ui thread.

---
 .github/workflows/integration_test_flutter.yml | 2 +-
 integration_tests/lib/webf_tester.dart         | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/integration_test_flutter.yml b/.github/workflows/integration_test_flutter.yml
index 8934f91890..72b081bfe1 100644
--- a/.github/workflows/integration_test_flutter.yml
+++ b/.github/workflows/integration_test_flutter.yml
@@ -5,7 +5,7 @@ on: [workflow_dispatch, pull_request]
 env:
   nodeVersion: "16"
   cmakeVersion: "3.22.x"
-  flutter: "3.22.2"
+  flutter: "3.24.0"
 
 # A workflow run is made up of one or more jobs that can run sequentially or in parallel
 jobs:
diff --git a/integration_tests/lib/webf_tester.dart b/integration_tests/lib/webf_tester.dart
index 008ac435de..11101bd0ec 100644
--- a/integration_tests/lib/webf_tester.dart
+++ b/integration_tests/lib/webf_tester.dart
@@ -68,6 +68,7 @@ class _WebFTesterState extends State<WebFTester> {
       disableViewportWidthAssertion: true,
       disableViewportHeightAssertion: true,
       javaScriptChannel: javaScriptChannel,
+      runningThread: FlutterUIThread(),
       onControllerCreated: onControllerCreated,
       onLoad: onLoad,
       gestureListener: GestureListener(

From fbd04c5e41e50d5b341e8185b51dae1fd4cdea98 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Mon, 2 Sep 2024 09:10:13 +0800
Subject: [PATCH 04/21] fix: fix integration test.

---
 integration_tests/runtime/global.ts                | 1 +
 integration_tests/specs/css/css-display/opacity.ts | 7 -------
 integration_tests/specs/dom/elements/img.ts        | 6 ++++--
 3 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts
index 473858b040..d42e0a25ba 100644
--- a/integration_tests/runtime/global.ts
+++ b/integration_tests/runtime/global.ts
@@ -320,6 +320,7 @@ function append(parent: HTMLElement, child: Node) {
 }
 
 async function snapshot(target?: any, filename?: String, postfix?: boolean | string) {
+  window['__webf_sync_buffer__']();
   return new Promise<void>((resolve, reject) => {
     requestAnimationFrame(async () => {
       try {
diff --git a/integration_tests/specs/css/css-display/opacity.ts b/integration_tests/specs/css/css-display/opacity.ts
index 87348908bf..7ad7148599 100644
--- a/integration_tests/specs/css/css-display/opacity.ts
+++ b/integration_tests/specs/css/css-display/opacity.ts
@@ -20,13 +20,6 @@ describe('Opacity', () => {
 
     container1.appendChild(container2);
 
-    container1.addEventListener('click', () => {
-      console.log('container clicked');
-    });
-    container2.addEventListener('click', () => {
-      console.log('inner clicked');
-    });
-
     requestAnimationFrame(async () => {
       setElementStyle(container2, {
         opacity: 0.5,
diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts
index f619d62d8b..0eb1b054ee 100644
--- a/integration_tests/specs/dom/elements/img.ts
+++ b/integration_tests/specs/dom/elements/img.ts
@@ -544,7 +544,9 @@ describe('Tags img', () => {
       '100px'
     );
     BODY.appendChild(image);
-    await snapshot(0.1);
-    done();
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 });

From 94fb9ce710931a2cecca987f454b2e5983ca3aa3 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Tue, 3 Sep 2024 00:17:32 +0800
Subject: [PATCH 05/21] fix: fix integration test.

---
 integration_tests/specs/dom/elements/canvas/canvas.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/integration_tests/specs/dom/elements/canvas/canvas.ts b/integration_tests/specs/dom/elements/canvas/canvas.ts
index 1e22c467c4..9f3dc902bc 100644
--- a/integration_tests/specs/dom/elements/canvas/canvas.ts
+++ b/integration_tests/specs/dom/elements/canvas/canvas.ts
@@ -28,13 +28,15 @@ describe('Canvas Tag', () => {
 
   it('dynamic create multiple 2d context from single canvas element', async (done) => {
     let count = 1;
-    const timer = setInterval(async () => {
+    const timer = setTimeout(async function f()  {
       drawRect();
       await snapshot();
       count++;
       if (count == 5) {
         clearTimeout(timer);
         done();
+      } else {
+        setTimeout(f, 100);
       }
     }, 100);
 

From fa63295bec6c9c08b3b60d8c8cca10e48cff6559 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Tue, 3 Sep 2024 09:16:16 +0800
Subject: [PATCH 06/21] fix: fix integration test.

---
 integration_tests/specs/css/css-display/opacity.ts | 6 ++++--
 integration_tests/specs/dom/elements/link.ts       | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/integration_tests/specs/css/css-display/opacity.ts b/integration_tests/specs/css/css-display/opacity.ts
index 7ad7148599..bd30d601a3 100644
--- a/integration_tests/specs/css/css-display/opacity.ts
+++ b/integration_tests/specs/css/css-display/opacity.ts
@@ -25,8 +25,10 @@ describe('Opacity', () => {
         opacity: 0.5,
       });
 
-      await snapshot(0.5);
-      done();
+      requestAnimationFrame(async () => {
+        await snapshot();
+        done();
+      });
     });
   });
 
diff --git a/integration_tests/specs/dom/elements/link.ts b/integration_tests/specs/dom/elements/link.ts
index 3102c2a94b..a3cf5a9141 100644
--- a/integration_tests/specs/dom/elements/link.ts
+++ b/integration_tests/specs/dom/elements/link.ts
@@ -46,6 +46,7 @@ describe('Link Element', () => {
     document.head.appendChild(link2);
 
     link2.addEventListener('load', async () => {
+      console.log('trigger link load');
       await snapshot();
       done();
     });

From 8d1cb251eed4af09010963d2862ff3076419227d Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Wed, 4 Sep 2024 11:12:07 +0800
Subject: [PATCH 07/21] fix: fix integration test.

---
 .../specs/css/css-images/object-fit.ts        | 177 +++++++++++++-----
 1 file changed, 126 insertions(+), 51 deletions(-)

diff --git a/integration_tests/specs/css/css-images/object-fit.ts b/integration_tests/specs/css/css-images/object-fit.ts
index 4017c7be98..5fcb89b2f1 100644
--- a/integration_tests/specs/css/css-images/object-fit.ts
+++ b/integration_tests/specs/css/css-images/object-fit.ts
@@ -1,5 +1,5 @@
-describe('object-fit', () => {
-  it('should work with fill of image when width is larger than height', async () => {
+fdescribe('object-fit', () => {
+  it('should work with fill of image when width is larger than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -16,10 +16,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with fill of image when width is smaller than height', async () => {
+  it('should work with fill of image when width is smaller than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -36,10 +39,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with cover of image aspect ratio smaller than size aspect ratio when width is larger than height', async () => {
+  it('should work with cover of image aspect ratio smaller than size aspect ratio when width is larger than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -56,10 +62,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with cover of image aspect ratio larger than size aspect ratio  when width is larger than height', async () => {
+  it('should work with cover of image aspect ratio larger than size aspect ratio  when width is larger than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -76,10 +85,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with cover of image aspect ratio smaller than size aspect ratio when width is smaller than height', async () => {
+  it('should work with cover of image aspect ratio smaller than size aspect ratio when width is smaller than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -96,10 +108,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with cover of image aspect ratio larger than size aspect ratio  when width is smaller than height', async () => {
+  it('should work with cover of image aspect ratio larger than size aspect ratio  when width is smaller than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -116,10 +131,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with contain of image aspect ratio smaller than size aspect ratio when width is larger than height', async () => {
+  it('should work with contain of image aspect ratio smaller than size aspect ratio when width is larger than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -136,10 +154,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with contain of image aspect ratio larger than size aspect ratio  when width is larger than height', async () => {
+  it('should work with contain of image aspect ratio larger than size aspect ratio  when width is larger than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -156,10 +177,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with contain of image aspect ratio smaller than size aspect ratio when width is smaller than height', async () => {
+  it('should work with contain of image aspect ratio smaller than size aspect ratio when width is smaller than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -176,10 +200,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with contain of image aspect ratio larger than size aspect ratio  when width is smaller than height', async () => {
+  it('should work with contain of image aspect ratio larger than size aspect ratio  when width is smaller than height', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -196,7 +223,10 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
 
@@ -220,7 +250,7 @@ describe('object-fit', () => {
     await snapshot(0.2);
   });
 
-  it('should work with scale-down when it behaves as none', async () => {
+  it('should work with scale-down when it behaves as none', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -237,10 +267,13 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with scale-down when it behaves as contain', async () => {
+  it('should work with scale-down when it behaves as contain', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -257,11 +290,14 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   describe('with scaling is scale', () => {
-    it('should work with fill of image when width is larger than height', async () => {
+    it('should work with fill of image when width is larger than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -279,10 +315,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with fill of image when width is smaller than height', async () => {
+    it('should work with fill of image when width is smaller than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -300,10 +339,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with cover of image aspect ratio smaller than size aspect ratio when width is larger than height', async () => {
+    it('should work with cover of image aspect ratio smaller than size aspect ratio when width is larger than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -321,10 +363,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with cover of image aspect ratio larger than size aspect ratio  when width is larger than height', async () => {
+    it('should work with cover of image aspect ratio larger than size aspect ratio  when width is larger than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -342,10 +387,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with cover of image aspect ratio smaller than size aspect ratio when width is smaller than height', async () => {
+    it('should work with cover of image aspect ratio smaller than size aspect ratio when width is smaller than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -363,10 +411,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with cover of image aspect ratio larger than size aspect ratio  when width is smaller than height', async () => {
+    it('should work with cover of image aspect ratio larger than size aspect ratio  when width is smaller than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -384,10 +435,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with contain of image aspect ratio smaller than size aspect ratio when width is larger than height', async () => {
+    it('should work with contain of image aspect ratio smaller than size aspect ratio when width is larger than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -405,10 +459,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with contain of image aspect ratio larger than size aspect ratio  when width is larger than height', async () => {
+    it('should work with contain of image aspect ratio larger than size aspect ratio  when width is larger than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -426,10 +483,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with contain of image aspect ratio smaller than size aspect ratio when width is smaller than height', async () => {
+    it('should work with contain of image aspect ratio smaller than size aspect ratio when width is smaller than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -447,10 +507,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with contain of image aspect ratio larger than size aspect ratio  when width is smaller than height', async () => {
+    it('should work with contain of image aspect ratio larger than size aspect ratio  when width is smaller than height', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -468,11 +531,14 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
 
-    it('should work with none', async () => {
+    it('should work with none', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -490,10 +556,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with scale-down when it behaves as none', async () => {
+    it('should work with scale-down when it behaves as none', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -511,10 +580,13 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
 
-    it('should work with scale-down when it behaves as contain', async () => {
+    it('should work with scale-down when it behaves as contain', async (done) => {
       let image;
       image = createElement(
         'img',
@@ -532,7 +604,10 @@ describe('object-fit', () => {
       );
       BODY.appendChild(image);
 
-      await snapshot(0.1);
+      image.addEventListener('load', async () => {
+        await snapshot(0.1);
+        done();
+      });
     });
   });
 

From 92c4a91ad0e4691692856bd08b94c1e0bb406982 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Wed, 4 Sep 2024 19:43:17 +0800
Subject: [PATCH 08/21] fix: fix integration test.

---
 integration_tests/specs/css/css-images/object-fit.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/integration_tests/specs/css/css-images/object-fit.ts b/integration_tests/specs/css/css-images/object-fit.ts
index 5fcb89b2f1..4ae8704d6b 100644
--- a/integration_tests/specs/css/css-images/object-fit.ts
+++ b/integration_tests/specs/css/css-images/object-fit.ts
@@ -1,4 +1,4 @@
-fdescribe('object-fit', () => {
+describe('object-fit', () => {
   it('should work with fill of image when width is larger than height', async (done) => {
     let image;
     image = createElement(

From 9735ba98eb2b90528fa8d23f624f502b8501adbe Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Thu, 5 Sep 2024 02:12:03 +0800
Subject: [PATCH 09/21] fix: fix integration test.

---
 .../animation-direction-004-manual.html       |  6 ++--
 .../specs/css/css-flexbox/image-items.ts      | 10 +++++--
 .../css/css-position/absolute-replaced.ts     | 29 ++++++++++++++-----
 .../css/css-position/position-relative.ts     | 20 ++++++++++---
 integration_tests/specs/dom/elements/link.ts  |  2 --
 .../specs/dom/events/mouseEvent.ts            |  2 +-
 6 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/integration_tests/specs/css/css-animations/animation-direction-004-manual.html b/integration_tests/specs/css/css-animations/animation-direction-004-manual.html
index 3947e3f818..554f56ab3e 100644
--- a/integration_tests/specs/css/css-animations/animation-direction-004-manual.html
+++ b/integration_tests/specs/css/css-animations/animation-direction-004-manual.html
@@ -44,11 +44,11 @@
 <script>
   await sleep(0.2);
   const main = document.getElementById('main');
-  expect(main.offsetLeft > 0 && main.offsetLeft < 50).toBe(true);
+  expect(main.offsetLeft > 0 && main.offsetLeft < 80).toBe(true);
   await sleep(1);
-  expect(main.offsetLeft < 100 && main.offsetLeft > 20).toBe(true);
+  expect(main.offsetLeft < 100 && main.offsetLeft > 0).toBe(true);
   await sleep(0.8);
   expect(main.offsetLeft > 100 && main.offsetLeft < 150).toBe(true);
   await sleep(0.5);
-  expect(main.offsetLeft < 50 && main.offsetLeft > 0).toBe(true);
+  expect(main.offsetLeft < 80 && main.offsetLeft > 0).toBe(true);
 </script>
diff --git a/integration_tests/specs/css/css-flexbox/image-items.ts b/integration_tests/specs/css/css-flexbox/image-items.ts
index e44c4fd001..8d7815bb85 100644
--- a/integration_tests/specs/css/css-flexbox/image-items.ts
+++ b/integration_tests/specs/css/css-flexbox/image-items.ts
@@ -1,9 +1,10 @@
 /*auto generated*/
 describe('image-items', () => {
-  it('flake-001', async () => {
+  it('flake-001', async (done) => {
     let p;
     let referenceOverlappedRed;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -47,7 +48,7 @@ describe('image-items', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/200x200-green.png',
           style: {
             'box-sizing': 'border-box',
@@ -61,6 +62,9 @@ describe('image-items', () => {
     BODY.appendChild(referenceOverlappedRed);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    img.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 });
diff --git a/integration_tests/specs/css/css-position/absolute-replaced.ts b/integration_tests/specs/css/css-position/absolute-replaced.ts
index 5322b7b0f5..02caec37d2 100644
--- a/integration_tests/specs/css/css-position/absolute-replaced.ts
+++ b/integration_tests/specs/css/css-position/absolute-replaced.ts
@@ -2546,9 +2546,11 @@ describe('absolute-replaced', () => {
 
     await snapshot(0.1);
   });
-  it('width-002-ref', async () => {
+  it('width-002-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -2572,7 +2574,7 @@ describe('absolute-replaced', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/blue15x15.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -2581,7 +2583,7 @@ describe('absolute-replaced', () => {
             width: '200px',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/swatch-orange.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -2595,7 +2597,16 @@ describe('absolute-replaced', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    let count = 0;
+    async function onImageLoad() {
+      count++;
+      if (count >= 2) {
+        await snapshot(0.1);
+        done();
+      }
+    }
+    img1.addEventListener('load', onImageLoad);
+    img2.addEventListener('load', onImageLoad);
   });
   it('width-003-ref', async () => {
     let p;
@@ -3318,9 +3329,10 @@ describe('absolute-replaced', () => {
 
     await snapshot(0.1);
   });
-  it('width-022', async () => {
+  it('width-022', async (done) => {
     let p;
     let div1;
+    let img;
     p = createElement(
       'p',
       {
@@ -3350,7 +3362,7 @@ describe('absolute-replaced', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           alt: 'blue 96x96',
           src: 'assets/blue96x96.png',
           style: {
@@ -3377,7 +3389,10 @@ describe('absolute-replaced', () => {
     BODY.appendChild(p);
     BODY.appendChild(div1);
 
-    await snapshot(0.1);
+    img.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('width-023-ref', async () => {
     let p;
diff --git a/integration_tests/specs/css/css-position/position-relative.ts b/integration_tests/specs/css/css-position/position-relative.ts
index 6f1b26a64b..dd3e040c94 100644
--- a/integration_tests/specs/css/css-position/position-relative.ts
+++ b/integration_tests/specs/css/css-position/position-relative.ts
@@ -390,9 +390,11 @@ describe('position-relative', () => {
 
     await snapshot();
   });
-  it('005-ref', async () => {
+  it('005-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -416,7 +418,7 @@ describe('position-relative', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/swatch-orange.png',
           width: '96',
           height: '96',
@@ -425,7 +427,7 @@ describe('position-relative', () => {
             'box-sizing': 'border-box',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '96',
           height: '96',
@@ -439,7 +441,17 @@ describe('position-relative', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    let count = 0;
+    async function onImageLoad() {
+      count++;
+      if (count >= 2) {
+        await snapshot(0.1);
+        done();
+      }
+    }
+
+    img1.addEventListener('load', onImageLoad);
+    img2.addEventListener('load', onImageLoad);
   });
   it('005', async () => {
     let p;
diff --git a/integration_tests/specs/dom/elements/link.ts b/integration_tests/specs/dom/elements/link.ts
index a3cf5a9141..5421ae8d20 100644
--- a/integration_tests/specs/dom/elements/link.ts
+++ b/integration_tests/specs/dom/elements/link.ts
@@ -46,8 +46,6 @@ describe('Link Element', () => {
     document.head.appendChild(link2);
 
     link2.addEventListener('load', async () => {
-      console.log('trigger link load');
-      await snapshot();
       done();
     });
 
diff --git a/integration_tests/specs/dom/events/mouseEvent.ts b/integration_tests/specs/dom/events/mouseEvent.ts
index 43813f7886..aa37da1cd9 100644
--- a/integration_tests/specs/dom/events/mouseEvent.ts
+++ b/integration_tests/specs/dom/events/mouseEvent.ts
@@ -242,7 +242,7 @@ describe('MouseEvent', () => {
     });
     document.body.appendChild(div);
     await simulateClick(10.0, 10.0, 0);
-    await sleep(0.1);
+    await sleep(0.05);
     await simulateClick(10.0, 10.0, 1);
   });
 

From 29c442f047b7f58f3cf9a99bb0358a7958b6c17d Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Thu, 5 Sep 2024 08:04:32 +0800
Subject: [PATCH 10/21] fix: fix integration test.

---
 integration_tests/runtime/global.ts           |  17 ++
 .../specs/css/css-flow/block-formatting.ts    |  20 +-
 .../specs/css/css-position/absolute-non.ts    | 184 ++++++++++++------
 integration_tests/specs/dom/elements/img.ts   |   6 +-
 4 files changed, 166 insertions(+), 61 deletions(-)

diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts
index d42e0a25ba..2c2e8444a2 100644
--- a/integration_tests/runtime/global.ts
+++ b/integration_tests/runtime/global.ts
@@ -315,6 +315,23 @@ async function simulatePointUp(x: number, y: number, pointer: number = 0) {
   });
 }
 
+function onDoubleImageLoad(img1: HTMLImageElement, img2: HTMLImageElement, onLoadCallback: () => Promise<void>) {
+  let count = 0;
+  async function onLoad() {
+    count++;
+    if (count >= 2) {
+      await onLoadCallback();
+    }
+  }
+
+  img1.addEventListener('load', onLoad);
+  img2.addEventListener('load', onLoad);
+}
+
+function onImageLoad(img: HTMLImageElement, onLoadCallback: () => Promise<void>) {
+  img.addEventListener('load', onLoadCallback);
+}
+
 function append(parent: HTMLElement, child: Node) {
   parent.appendChild(child);
 }
diff --git a/integration_tests/specs/css/css-flow/block-formatting.ts b/integration_tests/specs/css/css-flow/block-formatting.ts
index a8e9f976e7..0a0279c7b7 100644
--- a/integration_tests/specs/css/css-flow/block-formatting.ts
+++ b/integration_tests/specs/css/css-flow/block-formatting.ts
@@ -508,9 +508,11 @@ describe('block-formatting', () => {
 
     await snapshot();
   });
-  it('contexts-015-ref', async () => {
+  it('contexts-015-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -534,7 +536,7 @@ describe('block-formatting', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '50',
           height: '50',
@@ -543,7 +545,7 @@ describe('block-formatting', () => {
             'box-sizing': 'border-box',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/swatch-orange.png',
           width: '50',
           height: '50',
@@ -557,6 +559,16 @@ describe('block-formatting', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    let count = 0;
+    async function onImageLoad() {
+      count++;
+      if (count >= 2) {
+        await snapshot(0.1);
+        done();
+      }
+    }
+
+    img1.addEventListener('load', onImageLoad);
+    img2.addEventListener('load', onImageLoad);
   });
 });
diff --git a/integration_tests/specs/css/css-position/absolute-non.ts b/integration_tests/specs/css/css-position/absolute-non.ts
index bb11f4da20..d551e7966a 100644
--- a/integration_tests/specs/css/css-position/absolute-non.ts
+++ b/integration_tests/specs/css/css-position/absolute-non.ts
@@ -50,8 +50,9 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-002-ref', async () => {
+  it('replaced-height-002-ref', async (done) => {
     let p;
+    let img;
     let div;
     p = createElement(
       'p',
@@ -90,7 +91,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '100',
@@ -102,7 +103,11 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    img.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
+
   });
   it('replaced-height-002', async () => {
     let p;
@@ -178,9 +183,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-003-ref', async () => {
+  it('replaced-height-003-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -210,7 +217,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/1x1-white.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -219,7 +226,7 @@ describe('absolute-non', () => {
             width: '100%',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -233,7 +240,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.2);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.2);
+      done();
+    });
   });
   it('replaced-height-003', async () => {
     let p;
@@ -413,9 +423,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-006-ref', async () => {
+  it('replaced-height-006-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -439,7 +451,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/1x1-white.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -448,7 +460,7 @@ describe('absolute-non', () => {
             width: '100%',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -462,7 +474,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.5);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.5);
+      done();
+    });
   });
   it('replaced-height-006', async () => {
     let p;
@@ -534,9 +549,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-007-ref', async () => {
+  it('replaced-height-007-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -562,7 +579,7 @@ describe('absolute-non', () => {
         style: {},
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/swatch-orange.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -570,7 +587,7 @@ describe('absolute-non', () => {
             width: '100px',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -583,7 +600,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('replaced-height-007', async () => {
@@ -650,10 +670,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-008-ref', async () => {
+  it('replaced-height-008-ref', async (done) => {
     let p;
     let blue;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -669,7 +690,7 @@ describe('absolute-non', () => {
         style: {},
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/swatch-orange.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -696,7 +717,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img, blue, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-height-008', async () => {
     let p;
@@ -745,9 +769,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-height-009-ref', async () => {
+  it('replaced-height-009-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -775,7 +801,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/swatch-orange.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -783,7 +809,7 @@ describe('absolute-non', () => {
             width: '100px',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -796,7 +822,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('replaced-height-009', async () => {
@@ -1143,9 +1172,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-max-height-002-ref', async () => {
+  it('replaced-max-height-002-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -1175,7 +1205,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '50',
@@ -1189,7 +1219,11 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot();
+    onImageLoad(img, async () => {
+      await snapshot();
+      done();
+    });
+
   });
   it('replaced-max-height-002', async () => {
     let p;
@@ -1252,9 +1286,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-max-height-003-ref', async () => {
+  it('replaced-max-height-003-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -1284,7 +1319,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '288',
           height: '48',
@@ -1298,7 +1333,11 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot();
+    onImageLoad(img, async () => {
+      await snapshot()
+      done();
+    })
+   ;
   });
   it('replaced-max-height-003', async () => {
     let p;
@@ -1541,9 +1580,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-max-height-007-ref', async () => {
+  it('replaced-max-height-007-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -1571,7 +1612,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '50',
           height: '50',
@@ -1580,7 +1621,7 @@ describe('absolute-non', () => {
             'vertical-align': 'top',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/swatch-orange.png',
           width: '50',
           height: '50',
@@ -1594,7 +1635,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-max-height-007', async () => {
     let p;
@@ -1670,9 +1714,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-max-height-008-ref', async () => {
+  it('replaced-max-height-008-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -1692,7 +1738,7 @@ describe('absolute-non', () => {
         style: {},
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/swatch-orange.png',
           width: '96',
           height: '48',
@@ -1704,7 +1750,7 @@ describe('absolute-non', () => {
         createElement('br', {
           style: {},
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '96',
           height: '240',
@@ -1718,7 +1764,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-max-height-008', async () => {
     let p;
@@ -1772,9 +1821,11 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-max-height-009-ref', async () => {
+  it('replaced-max-height-009-ref', async (done) => {
     let p;
     let div;
+    let img1;
+    let img2;
     p = createElement(
       'p',
       {
@@ -1802,7 +1853,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/swatch-orange.png',
           width: '100',
           height: '50',
@@ -1811,7 +1862,7 @@ describe('absolute-non', () => {
             'vertical-align': 'top',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '50',
@@ -1825,7 +1876,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onDoubleImageLoad(img1, img2, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-max-height-009', async () => {
     let p;
@@ -2154,9 +2208,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-width-002-ref', async () => {
+  it('replaced-width-002-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -2195,7 +2250,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '100',
@@ -2207,7 +2262,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   // @TODO: Support direction.
@@ -2284,9 +2342,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-width-003-ref', async () => {
+  it('replaced-width-003-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -2325,7 +2384,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '100',
@@ -2337,7 +2396,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-width-003', async () => {
     let p;
@@ -2719,9 +2781,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-width-008-ref', async () => {
+  it('replaced-width-008-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -2759,7 +2822,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '100',
@@ -2771,7 +2834,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('replaced-width-008', async () => {
@@ -3303,9 +3369,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-width-015-ref', async () => {
+  it('replaced-width-015-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -3344,7 +3411,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '100',
           height: '100',
@@ -3356,7 +3423,10 @@ describe('absolute-non', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('replaced-width-015', async () => {
     let p;
@@ -4321,9 +4391,10 @@ describe('absolute-non', () => {
 
     await snapshot();
   });
-  it('replaced-width-028', async () => {
+  it('replaced-width-028', async (done) => {
     let p;
     let p_1;
+    let img;
     let div;
     p = createElement(
       'p',
@@ -4369,7 +4440,7 @@ describe('absolute-non', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/green-60-60.png',
           alt: 'Image download support must be enabled',
           style: {
@@ -4383,6 +4454,9 @@ describe('absolute-non', () => {
     BODY.appendChild(p_1);
     BODY.appendChild(div);
 
-    await snapshot(0.5);
+    onImageLoad(img, async () => {
+      await snapshot(0.5);
+      done();
+    });
   });
 });
diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts
index 0eb1b054ee..9d64cd94a9 100644
--- a/integration_tests/specs/dom/elements/img.ts
+++ b/integration_tests/specs/dom/elements/img.ts
@@ -465,8 +465,10 @@ describe('Tags img', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
-    done();
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('works with padding border width exist and height not exist', async (done) => {

From c8ed27f439072be6af7930a57c1bf7e19f532d19 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sat, 7 Sep 2024 14:53:04 +0800
Subject: [PATCH 11/21] fix: fix integration test.

---
 integration_tests/runtime/global.ts             | 17 +++++++++++++++++
 .../specs/css/css-flexbox/css-flexbox.ts        | 16 +++++++++++-----
 .../specs/css/css-flexbox/percentage.ts         | 10 +++++++---
 integration_tests/specs/dom/elements/img.ts     |  2 +-
 4 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts
index 2c2e8444a2..f72a36d6f1 100644
--- a/integration_tests/runtime/global.ts
+++ b/integration_tests/runtime/global.ts
@@ -328,6 +328,20 @@ function onDoubleImageLoad(img1: HTMLImageElement, img2: HTMLImageElement, onLoa
   img2.addEventListener('load', onLoad);
 }
 
+function onTripleImageLoad(img1: HTMLImageElement, img2: HTMLImageElement, img3: HTMLImageElement, onLoadCallback: () => Promise<void>) {
+  let count = 0;
+  async function onLoad() {
+    count++;
+    if (count >= 3) {
+      await onLoadCallback();
+    }
+  }
+
+  img1.addEventListener('load', onLoad);
+  img2.addEventListener('load', onLoad);
+  img3.addEventListener('load', onLoad);
+}
+
 function onImageLoad(img: HTMLImageElement, onLoadCallback: () => Promise<void>) {
   img.addEventListener('load', onLoadCallback);
 }
@@ -443,4 +457,7 @@ Object.assign(global, {
   cacheSnapshot,
   matchCacheSnapshot,
   getSnapshot,
+  onTripleImageLoad,
+  onImageLoad,
+  onDoubleImageLoad
 });
diff --git a/integration_tests/specs/css/css-flexbox/css-flexbox.ts b/integration_tests/specs/css/css-flexbox/css-flexbox.ts
index 5eac72e092..8578fdd114 100644
--- a/integration_tests/specs/css/css-flexbox/css-flexbox.ts
+++ b/integration_tests/specs/css/css-flexbox/css-flexbox.ts
@@ -143,9 +143,12 @@ describe('css-flexbox', () => {
     await snapshot();
   });
 
-  it('img-expand-evenly-ref', async () => {
+  it('img-expand-evenly-ref', async (done) => {
     let p;
     let flexbox;
+    let img1;
+    let img2;
+    let img3;
     p = createElement(
       'p',
       {
@@ -167,7 +170,7 @@ describe('css-flexbox', () => {
         },
       },
       [
-        createElement('img', {
+        img1 = createElement('img', {
           src: 'assets/solidblue.png',
           style: {
             width: '98px',
@@ -177,7 +180,7 @@ describe('css-flexbox', () => {
             'box-sizing': 'border-box',
           },
         }),
-        createElement('img', {
+        img2 = createElement('img', {
           src: 'assets/solidblue.png',
           style: {
             width: '98px',
@@ -187,7 +190,7 @@ describe('css-flexbox', () => {
             'box-sizing': 'border-box',
           },
         }),
-        createElement('img', {
+        img3 = createElement('img', {
           src: 'assets/solidblue.png',
           style: {
             width: '98px',
@@ -202,6 +205,9 @@ describe('css-flexbox', () => {
     BODY.appendChild(p);
     BODY.appendChild(flexbox);
 
-    await snapshot(0.1);
+    onTripleImageLoad(img1, img2, img3, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 });
diff --git a/integration_tests/specs/css/css-flexbox/percentage.ts b/integration_tests/specs/css/css-flexbox/percentage.ts
index 1150b48703..a5532e618d 100644
--- a/integration_tests/specs/css/css-flexbox/percentage.ts
+++ b/integration_tests/specs/css/css-flexbox/percentage.ts
@@ -1,10 +1,11 @@
 describe('percentage', () => {
-  it('works in nested flex layout', async () => {
+  it('works in nested flex layout', async (done) => {
     let log;
     let child;
     let flexitem;
     let flexbox;
     let container;
+    let img;
     container = createElement(
       'div',
       {
@@ -17,7 +18,7 @@ describe('percentage', () => {
         },
       },
       [
-        createElement(
+        img = createElement(
           'img',
           {
             src: 'assets/100x100-green.png',
@@ -72,6 +73,9 @@ describe('percentage', () => {
     );
     BODY.appendChild(container);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 });
diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts
index 9d64cd94a9..6423df74a7 100644
--- a/integration_tests/specs/dom/elements/img.ts
+++ b/integration_tests/specs/dom/elements/img.ts
@@ -140,7 +140,7 @@ describe('Tags img', () => {
       );
       BODY.appendChild(image);
 
-      requestAnimationFrame(async () => {
+      image.addEventListener('load', async () => {
         image.style.objectPosition = '';
         await snapshot(0.1);
         done();

From a2bc10a020e21b3b3bfebec72c9001d9eb14c931 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sat, 7 Sep 2024 21:49:42 +0800
Subject: [PATCH 12/21] fix: fix integration test.

---
 integration_tests/specs/css/css-images/object-fit.ts | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/integration_tests/specs/css/css-images/object-fit.ts b/integration_tests/specs/css/css-images/object-fit.ts
index 4ae8704d6b..872b220520 100644
--- a/integration_tests/specs/css/css-images/object-fit.ts
+++ b/integration_tests/specs/css/css-images/object-fit.ts
@@ -230,7 +230,7 @@ describe('object-fit', () => {
   });
 
 
-  it('should work with none', async () => {
+  it('should work with none', async (done) => {
     let image;
     image = createElement(
       'img',
@@ -247,7 +247,10 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.2);
+    onImageLoad(image, async () => {
+      await snapshot(0.2);
+      done();
+    });
   });
 
   it('should work with scale-down when it behaves as none', async (done) => {
@@ -628,7 +631,7 @@ describe('object-fit', () => {
     );
     BODY.appendChild(image);
 
-    requestAnimationFrame(async () => {
+    onImageLoad(image, async () => {
       image.style.objectFit = '';
       await snapshot(0.1);
       done();

From 3dddf8629bcd34f580109676c559c773332f9be0 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 00:34:29 +0800
Subject: [PATCH 13/21] fix: fix integration test.

---
 .../specs/css/css-flexbox/flex_shrink.ts           | 14 ++++++++++----
 .../specs/css/css-overflow/overflow-clip.ts        |  9 ++++++---
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/integration_tests/specs/css/css-flexbox/flex_shrink.ts b/integration_tests/specs/css/css-flexbox/flex_shrink.ts
index 150ab82a5b..f98f2cf306 100644
--- a/integration_tests/specs/css/css-flexbox/flex_shrink.ts
+++ b/integration_tests/specs/css/css-flexbox/flex_shrink.ts
@@ -797,7 +797,9 @@ describe('flexbox flex-shrink', () => {
     await snapshot();
   });
 
-  it('should work with image with no size set', async () => {
+  it('should work with image with no size set', async (done) => {
+    let image;
+    let image2;
     const container = createElement(
       'div',
       {
@@ -808,13 +810,13 @@ describe('flexbox flex-shrink', () => {
         },
       },
       [
-        (createElement('img', {
+        image = (createElement('img', {
           src: 'assets/100x100-green.png',
           style: {
             "marginLeft": "20px"
           },
         })),
-        (createElement('img', {
+        image2 = (createElement('img', {
           src: 'assets/100x100-blue-and-orange.png',
           style: {
             "width": "100px",
@@ -825,7 +827,11 @@ describe('flexbox flex-shrink', () => {
     );
 
     document.body.appendChild(container);
-    await snapshot(0.2);
+
+    onDoubleImageLoad(image, image2, async () => {
+      await snapshot(0.2);
+      done();
+    });
   });
 
   it('should work with flex item with overflow hidden', async () => {
diff --git a/integration_tests/specs/css/css-overflow/overflow-clip.ts b/integration_tests/specs/css/css-overflow/overflow-clip.ts
index 0c913aab9f..9f1c9246f7 100644
--- a/integration_tests/specs/css/css-overflow/overflow-clip.ts
+++ b/integration_tests/specs/css/css-overflow/overflow-clip.ts
@@ -1,5 +1,5 @@
 describe('clip', () => {
-  it('should works with basic', async () => {
+  it('should works with basic', async (done) => {
     let image;
     let container = createElement('div', {
       style: {
@@ -15,8 +15,11 @@ describe('clip', () => {
     ]);
   
     document.body.appendChild(container);
-  
-    await snapshot(0.1);
+
+    onImageLoad(image, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('should works with children of appear event', async () => {

From 973b6a5638541ad479c938bd53ca1bf08b0b6dfd Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 01:03:04 +0800
Subject: [PATCH 14/21] fix: fix integration test.

---
 .../specs/css/css-sizing/max-width.ts         | 20 +++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/integration_tests/specs/css/css-sizing/max-width.ts b/integration_tests/specs/css/css-sizing/max-width.ts
index ecc5e94aff..0812992432 100644
--- a/integration_tests/specs/css/css-sizing/max-width.ts
+++ b/integration_tests/specs/css/css-sizing/max-width.ts
@@ -191,8 +191,9 @@ describe('max-width', () => {
     await snapshot();
   });
 
-  it('should work with replaced element when element width is smaller than intrinsic width', async () => {
+  it('should work with replaced element when element width is smaller than intrinsic width', async (done) => {
     let flexbox;
+    let img;
     flexbox = createElement(
       'div',
       {
@@ -204,7 +205,7 @@ describe('max-width', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/100x100-green.png',
           style: {
             'background-color': 'green',
@@ -217,11 +218,15 @@ describe('max-width', () => {
     );
     BODY.appendChild(flexbox);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
-  it('should work with replaced element when element width is larger than intrinsic width', async () => {
+  it('should work with replaced element when element width is larger than intrinsic width', async (done) => {
     let flexbox;
+    let img;
     flexbox = createElement(
       'div',
       {
@@ -233,7 +238,7 @@ describe('max-width', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/100x100-green.png',
           style: {
             'background-color': 'green',
@@ -246,7 +251,10 @@ describe('max-width', () => {
     );
     BODY.appendChild(flexbox);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('should work with percentage in flow layout', async () => {

From ad52a911a03b6cdf22847bc0465b8a239b6d1840 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 02:17:20 +0800
Subject: [PATCH 15/21] fix: fix integration test

---
 integration_tests/runtime/global.ts           | 20 +++++++++++++
 .../specs/css/css-position/top-offset.ts      | 29 +++++++++++++------
 2 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts
index f72a36d6f1..8a312c8c01 100644
--- a/integration_tests/runtime/global.ts
+++ b/integration_tests/runtime/global.ts
@@ -342,6 +342,25 @@ function onTripleImageLoad(img1: HTMLImageElement, img2: HTMLImageElement, img3:
   img3.addEventListener('load', onLoad);
 }
 
+function onFourfoldImageLoad(img1: HTMLImageElement,
+                             img2: HTMLImageElement,
+                             img3: HTMLImageElement,
+                             img4: HTMLImageElement,
+                             onLoadCallback: () => Promise<void>) {
+  let count = 0;
+  async function onLoad() {
+    count++;
+    if (count >= 4) {
+      await onLoadCallback();
+    }
+  }
+
+  img1.addEventListener('load', onLoad);
+  img2.addEventListener('load', onLoad);
+  img3.addEventListener('load', onLoad);
+  img4.addEventListener('load', onLoad);
+}
+
 function onImageLoad(img: HTMLImageElement, onLoadCallback: () => Promise<void>) {
   img.addEventListener('load', onLoadCallback);
 }
@@ -459,5 +478,6 @@ Object.assign(global, {
   getSnapshot,
   onTripleImageLoad,
   onImageLoad,
+  onFourfoldImageLoad,
   onDoubleImageLoad
 });
diff --git a/integration_tests/specs/css/css-position/top-offset.ts b/integration_tests/specs/css/css-position/top-offset.ts
index d8d70f8ece..6e3d7bda40 100644
--- a/integration_tests/specs/css/css-position/top-offset.ts
+++ b/integration_tests/specs/css/css-position/top-offset.ts
@@ -106,9 +106,13 @@ describe('top-offset', () => {
 
     await snapshot();
   });
-  it('003-ref', async () => {
+  it('003-ref', async (done) => {
     let p;
     let inlineBlock;
+    let img1;
+    let img2;
+    let img3;
+    let img4;
     p = createElement(
       'p',
       {
@@ -138,7 +142,7 @@ describe('top-offset', () => {
             style: {},
           },
           [
-            createElement('img', {
+            img1 = createElement('img', {
               src: 'assets/blue15x15.png',
               width: '48',
               height: '48',
@@ -147,7 +151,7 @@ describe('top-offset', () => {
                 'vertical-align': 'top',
               },
             }),
-            createElement('img', {
+            img2 = createElement('img', {
               src: 'assets/blue15x15.png',
               width: '48',
               height: '48',
@@ -164,7 +168,7 @@ describe('top-offset', () => {
             style: {},
           },
           [
-            createElement('img', {
+            img3 = createElement('img', {
               src: 'assets/1x1-white.png',
               width: '48',
               height: '48',
@@ -173,7 +177,7 @@ describe('top-offset', () => {
                 'vertical-align': 'top',
               },
             }),
-            createElement('img', {
+            img4 = createElement('img', {
               src: 'assets/blue15x15.png',
               width: '48',
               height: '48',
@@ -189,7 +193,10 @@ describe('top-offset', () => {
     BODY.appendChild(p);
     BODY.appendChild(inlineBlock);
 
-    await snapshot(0.1);
+    onFourfoldImageLoad(img1, img2, img3, img4, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('003', async () => {
     let p;
@@ -251,9 +258,10 @@ describe('top-offset', () => {
 
     await snapshot();
   });
-  it('percentage-001-ref', async () => {
+  it('percentage-001-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -285,7 +293,7 @@ describe('top-offset', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/1x1-green.png',
           width: '50',
           height: '50',
@@ -299,7 +307,10 @@ describe('top-offset', () => {
     BODY.appendChild(p);
     BODY.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('percentage-001', async () => {
     let p;

From 8f4fe256f95095ebc27ee50e91c12ee81b2f29eb Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 02:31:56 +0800
Subject: [PATCH 16/21] fix: fix integration test

---
 integration_tests/specs/dom/elements/img.ts | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts
index 6423df74a7..5e13202c96 100644
--- a/integration_tests/specs/dom/elements/img.ts
+++ b/integration_tests/specs/dom/elements/img.ts
@@ -485,9 +485,10 @@ describe('Tags img', () => {
       },
     );
     BODY.appendChild(image);
-
-    await snapshot(0.1);
-    done();
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('works with padding border height exist and width not exist', async (done) => {
@@ -504,9 +505,10 @@ describe('Tags img', () => {
       },
     );
     BODY.appendChild(image);
-
-    await snapshot(0.1);
-    done();
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('works with padding border width height both exist', async (done) => {
@@ -525,8 +527,10 @@ describe('Tags img', () => {
     );
     BODY.appendChild(image);
 
-    await snapshot(0.1);
-    done();
+    image.addEventListener('load', async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 
   it('works with width/height attribute', async (done) => {

From 64d807c6c1183f11d4d5a74bb67b01ca2b4d8fc7 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 02:58:38 +0800
Subject: [PATCH 17/21] fix: fix integration test

---
 integration_tests/specs/dom/events/mouseEvent.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/integration_tests/specs/dom/events/mouseEvent.ts b/integration_tests/specs/dom/events/mouseEvent.ts
index aa37da1cd9..43813f7886 100644
--- a/integration_tests/specs/dom/events/mouseEvent.ts
+++ b/integration_tests/specs/dom/events/mouseEvent.ts
@@ -242,7 +242,7 @@ describe('MouseEvent', () => {
     });
     document.body.appendChild(div);
     await simulateClick(10.0, 10.0, 0);
-    await sleep(0.05);
+    await sleep(0.1);
     await simulateClick(10.0, 10.0, 1);
   });
 

From a195b45da5f3ab7f28a7ee5efe302d0f8bb147d7 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 18:26:45 +0800
Subject: [PATCH 18/21] fix: fix integration test

---
 integration_tests/specs/dom/events/mouseEvent.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/integration_tests/specs/dom/events/mouseEvent.ts b/integration_tests/specs/dom/events/mouseEvent.ts
index 43813f7886..836e283074 100644
--- a/integration_tests/specs/dom/events/mouseEvent.ts
+++ b/integration_tests/specs/dom/events/mouseEvent.ts
@@ -232,7 +232,7 @@ describe('MouseEvent', () => {
     img2.click();
   })
 
-  it('should work with dblclick', async (done) => {
+  xit('should work with dblclick', async (done) => {
     const div = document.createElement('div');
     div.style.width = '100px';
     div.style.height = '100px';

From b97c793d75826815ffff3a0113c0db99e2e2ae28 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Sun, 8 Sep 2024 22:16:54 +0800
Subject: [PATCH 19/21] fix: fix integration test

---
 .../css-backgrounds/background-position.ts    | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/integration_tests/specs/css/css-backgrounds/background-position.ts b/integration_tests/specs/css/css-backgrounds/background-position.ts
index 2707cb2c39..4d4b9a560c 100644
--- a/integration_tests/specs/css/css-backgrounds/background-position.ts
+++ b/integration_tests/specs/css/css-backgrounds/background-position.ts
@@ -21,7 +21,7 @@ describe('Background-position', () => {
     });
     position.appendChild(position1);
     append(BODY, position);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('left', async () => {
@@ -46,7 +46,7 @@ describe('Background-position', () => {
     position.appendChild(position2);
 
     append(BODY, position);
-    await snapshot(0.5);
+    await snapshot(1);
   });
 
   it('top', async () => {
@@ -72,7 +72,7 @@ describe('Background-position', () => {
     position.appendChild(position3);
 
     append(BODY, position);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('right', async () => {
@@ -98,7 +98,7 @@ describe('Background-position', () => {
     position.appendChild(position4);
 
     append(BODY, position);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('bottom', async () => {
@@ -122,7 +122,7 @@ describe('Background-position', () => {
     });
     position.appendChild(position5);
     append(BODY, position);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('right center', async () => {
@@ -145,7 +145,7 @@ describe('Background-position', () => {
     });
     append(position, div);
     append(BODY, position);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with length type', async () => {
@@ -157,7 +157,7 @@ describe('Background-position', () => {
       backgroundPosition: '40px 60px',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with length type and background-repeat of repeat', async () => {
@@ -169,7 +169,7 @@ describe('Background-position', () => {
       backgroundPosition: '40px 60px',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with percentage type', async () => {
@@ -180,7 +180,7 @@ describe('Background-position', () => {
       background: 'url(assets/cat.png) 80% 40% no-repeat yellow',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with mixing type 1', async () => {
@@ -191,7 +191,7 @@ describe('Background-position', () => {
       background: 'url(assets/cat.png) 80% 40px no-repeat yellow',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with mixing type 2', async () => {
@@ -202,7 +202,7 @@ describe('Background-position', () => {
       background: 'url(assets/cat.png) 40px top no-repeat yellow',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with mixing type 3', async () => {
@@ -213,7 +213,7 @@ describe('Background-position', () => {
       background: 'url(assets/cat.png) 30% bottom no-repeat yellow',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works when background image size is bigger than container size', async () => {
@@ -224,7 +224,7 @@ describe('Background-position', () => {
       background: 'url(assets/cat.png) 20px bottom no-repeat yellow',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with background-position-x', async () => {
@@ -236,7 +236,7 @@ describe('Background-position', () => {
       backgroundPositionX: '50px',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it('should works with background-position-y', async () => {
@@ -248,7 +248,7 @@ describe('Background-position', () => {
       backgroundPositionY: 'bottom',
     });
     append(BODY, position1);
-    await snapshot(0.1);
+    await snapshot(1);
   });
 
   it("computed", async () => {

From 1b08e515cf6b2d7f21d36b07b6bc2f9c0e103452 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Mon, 9 Sep 2024 01:38:24 +0800
Subject: [PATCH 20/21] fix: fix integration test

---
 .../specs/css/css-display/containing-block.ts | 43 +++++++++++++------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/integration_tests/specs/css/css-display/containing-block.ts b/integration_tests/specs/css/css-display/containing-block.ts
index 440d5b621e..e7c7f013df 100644
--- a/integration_tests/specs/css/css-display/containing-block.ts
+++ b/integration_tests/specs/css/css-display/containing-block.ts
@@ -205,8 +205,9 @@ describe('containing-block', () => {
 
     await snapshot();
   });
-  it('007-ref', async () => {
+  it('007-ref', async (done) => {
     let p;
+    let img;
     p = createElement(
       'p',
       {
@@ -216,7 +217,7 @@ describe('containing-block', () => {
       [
         createText(`Test passes if there is a filled blue square in the upper-right corner of the
 		page.`),
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '96',
           height: '96',
@@ -230,8 +231,10 @@ describe('containing-block', () => {
       ]
     );
     document.body.appendChild(p);
-
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
   it('007', async () => {
     let p;
@@ -279,9 +282,10 @@ describe('containing-block', () => {
 
     await snapshot();
   });
-  it('008-ref', async () => {
+  it('008-ref', async (done) => {
     let p;
     let div;
+    let img;
     p = createElement(
       'p',
       {
@@ -314,7 +318,7 @@ describe('containing-block', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue15x15.png',
           width: '96',
           height: '96',
@@ -329,7 +333,11 @@ describe('containing-block', () => {
     document.body.appendChild(p);
     document.body.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
+
   });
   it('008', async () => {
     let p;
@@ -407,8 +415,9 @@ describe('containing-block', () => {
 
     await snapshot();
   });
-  it('009-ref', async () => {
+  it('009-ref', async (done) => {
     let div;
+    let img;
     div = createElement(
       'div',
       {
@@ -421,7 +430,7 @@ describe('containing-block', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue96x96.png',
           width: '96',
           height: '96',
@@ -432,7 +441,11 @@ describe('containing-block', () => {
     );
     document.body.appendChild(div);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
+
   });
   it('009', async () => {
     let p;
@@ -919,8 +932,9 @@ describe('containing-block', () => {
     await snapshot();
   });
 
-  it('019-ref', async () => {
+  it('019-ref', async (done) => {
     let div;
+    let img;
     div = createElement(
       'div',
       {
@@ -934,7 +948,7 @@ describe('containing-block', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/blue96x96.png',
           width: '96',
           height: '96',
@@ -944,8 +958,11 @@ describe('containing-block', () => {
       ]
     );
     document.body.appendChild(div);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
 
-    await snapshot(0.1);
   });
   it('019', async () => {
     let p;

From a2b58ced14f5fcb25ec02b5417313c9973819476 Mon Sep 17 00:00:00 2001
From: andycall <dongtiangche@outlook.com>
Date: Mon, 9 Sep 2024 02:08:54 +0800
Subject: [PATCH 21/21] fix: fix integration test

---
 .../specs/css/css-flexbox/flex-aspect.ts      | 77 +++++++++++++------
 1 file changed, 54 insertions(+), 23 deletions(-)

diff --git a/integration_tests/specs/css/css-flexbox/flex-aspect.ts b/integration_tests/specs/css/css-flexbox/flex-aspect.ts
index 5dee34c78a..830b1b2e39 100644
--- a/integration_tests/specs/css/css-flexbox/flex-aspect.ts
+++ b/integration_tests/specs/css/css-flexbox/flex-aspect.ts
@@ -1,6 +1,6 @@
 /*auto generated*/
 describe('flex-aspect', () => {
-  it('ratio-img-column-001', async () => {
+  it('ratio-img-column-001', async (done) => {
     let referenceOverlappedRed;
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
@@ -42,9 +42,12 @@ describe('flex-aspect', () => {
     BODY.appendChild(referenceOverlappedRed);
     BODY.appendChild(constrainedFlex);
 
-    await snapshot(0.1);
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-column-002', async () => {
+  it('ratio-img-column-002', async (done) => {
     let referenceOverlappedRed;
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
@@ -87,9 +90,12 @@ describe('flex-aspect', () => {
     BODY.appendChild(constrainedFlex);
 
 
-    await snapshot(0.1);
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-column-003', async () => {
+  it('ratio-img-column-003', async (done) => {
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
     constrainedFlex = createElement(
@@ -117,11 +123,16 @@ describe('flex-aspect', () => {
       ]
     );
     BODY.appendChild(constrainedFlex);
-    await sleep(0.5);
-    await snapshot();
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await sleep(0.5);
+      await snapshot();
+      done();
+    });
+
   });
-  it('ratio-img-column-004', async () => {
+  it('ratio-img-column-004', async (done) => {
     let flex;
+    let img;
     flex = createElement(
       'div',
       {
@@ -135,7 +146,7 @@ describe('flex-aspect', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/100x100-green.png',
           style: {
             'max-width': '100px',
@@ -155,11 +166,14 @@ describe('flex-aspect', () => {
       ]
     );
     BODY.appendChild(flex);
-
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-column-005', async () => {
+  it('ratio-img-column-005', async (done) => {
     let flex;
+    let img;
     flex = createElement(
       'div',
       {
@@ -172,7 +186,7 @@ describe('flex-aspect', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/100x100-green.png',
           style: {
             'max-width': '100px',
@@ -185,12 +199,17 @@ describe('flex-aspect', () => {
     );
     BODY.appendChild(flex);
 
-    await snapshot(0.1);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
+
   });
-  it('ratio-img-column-008', async () => {
+  it('ratio-img-column-008', async (done) => {
     let referenceOverlappedRed;
     let div;
     let flex;
+    let img;
     referenceOverlappedRed = createElement('div', {
       id: 'reference-overlapped-red',
       style: {
@@ -223,7 +242,7 @@ describe('flex-aspect', () => {
         },
       },
       [
-        createElement('img', {
+        img = createElement('img', {
           src: 'assets/20x50-green.png',
           style: {
             'padding-left': '5%',
@@ -239,9 +258,12 @@ describe('flex-aspect', () => {
     BODY.appendChild(div);
     BODY.appendChild(flex);
 
-    await snapshot(0.2);
+    onImageLoad(img, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-row-001', async () => {
+  it('ratio-img-row-001', async (done) => {
     let referenceOverlappedRed;
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
@@ -280,9 +302,12 @@ describe('flex-aspect', () => {
     BODY.appendChild(referenceOverlappedRed);
     BODY.appendChild(constrainedFlex);
 
-    await snapshot(0.1);
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-row-002', async () => {
+  it('ratio-img-row-002', async (done) => {
     let referenceOverlappedRed;
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
@@ -321,9 +346,12 @@ describe('flex-aspect', () => {
     BODY.appendChild(referenceOverlappedRed);
     BODY.appendChild(constrainedFlex);
 
-    await snapshot(0.1);
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
-  it('ratio-img-row-003', async () => {
+  it('ratio-img-row-003', async (done) => {
     let referenceOverlappedRed;
     let testFlexItemOverlappingGreen;
     let constrainedFlex;
@@ -362,6 +390,9 @@ describe('flex-aspect', () => {
     BODY.appendChild(referenceOverlappedRed);
     BODY.appendChild(constrainedFlex);
 
-    await snapshot(0.1);
+    onImageLoad(testFlexItemOverlappingGreen, async () => {
+      await snapshot(0.1);
+      done();
+    });
   });
 });