diff --git a/__tests__/html2/middleware/avatar/backwardCompatibility.html b/__tests__/html2/middleware/avatar/backwardCompatibility.html
index c1df5f8971..473a5e9d65 100644
--- a/__tests__/html2/middleware/avatar/backwardCompatibility.html
+++ b/__tests__/html2/middleware/avatar/backwardCompatibility.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/addOrReplace.html b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/addOrReplace.html
index 2da4bad78d..a047c99d07 100644
--- a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/addOrReplace.html
+++ b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/addOrReplace.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/changing.html b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/changing.html
index a194772910..751974e871 100644
--- a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/changing.html
+++ b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/changing.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/decorate.html b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/decorate.html
index 2da4bad78d..a047c99d07 100644
--- a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/decorate.html
+++ b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/decorate.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/styleOptions.html b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/styleOptions.html
index 17d075a48d..c376eaceaa 100644
--- a/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/styleOptions.html
+++ b/__tests__/html2/middleware/avatar/legacyAvatarMiddleware/styleOptions.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser, styleOptions }) =>
diff --git a/__tests__/html2/middleware/avatar/polymiddleware/addOrReplace.html b/__tests__/html2/middleware/avatar/polymiddleware/addOrReplace.html
index 82de8c9a07..a4d05abc6f 100644
--- a/__tests__/html2/middleware/avatar/polymiddleware/addOrReplace.html
+++ b/__tests__/html2/middleware/avatar/polymiddleware/addOrReplace.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const PolymiddlewareAvatar = ({ activity, children }) =>
diff --git a/__tests__/html2/middleware/avatar/polymiddleware/changing.html b/__tests__/html2/middleware/avatar/polymiddleware/changing.html
index 20f6db9964..a81789a159 100644
--- a/__tests__/html2/middleware/avatar/polymiddleware/changing.html
+++ b/__tests__/html2/middleware/avatar/polymiddleware/changing.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const PolymiddlewareAvatar = ({ activity, children }) =>
diff --git a/__tests__/html2/middleware/avatar/polymiddleware/decorate.html b/__tests__/html2/middleware/avatar/polymiddleware/decorate.html
index 743e3adb5d..bd9cf29108 100644
--- a/__tests__/html2/middleware/avatar/polymiddleware/decorate.html
+++ b/__tests__/html2/middleware/avatar/polymiddleware/decorate.html
@@ -24,7 +24,7 @@
import { createElement } from 'react';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -33,6 +33,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const PolymiddlewareAvatar = ({ activity, children }) =>
diff --git a/__tests__/html2/middleware/avatar/renderProxy.html b/__tests__/html2/middleware/avatar/renderProxy.html
index 8e258df1cf..872b45205c 100644
--- a/__tests__/html2/middleware/avatar/renderProxy.html
+++ b/__tests__/html2/middleware/avatar/renderProxy.html
@@ -31,7 +31,7 @@
import { createRoot } from 'react-dom/client';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -40,6 +40,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/__tests__/html2/middleware/avatar/useBuildRenderAvatarCallback.html b/__tests__/html2/middleware/avatar/useBuildRenderAvatarCallback.html
index 60199cc9b4..cac579b2d3 100644
--- a/__tests__/html2/middleware/avatar/useBuildRenderAvatarCallback.html
+++ b/__tests__/html2/middleware/avatar/useBuildRenderAvatarCallback.html
@@ -31,7 +31,7 @@
import { createRoot } from 'react-dom/client';
const {
- testHelpers: { createDirectLineEmulator }
+ testHelpers: { createDirectLineEmulator, preloadImages }
} = window;
testHelpers.hideKnownError();
@@ -40,6 +40,9 @@
window.WebChat = { createStoreWithOptions, testIds };
run(async function () {
+ // Decode avatar images before they are rendered, so screenshots are stable.
+ await preloadImages('/assets/bot-avatar.jpg', '/assets/user-avatar.jpg');
+
const { directLine, store } = createDirectLineEmulator();
const MiddlewareAvatar = ({ activity, children, fromUser }) =>
diff --git a/packages/test/page-object/src/globals/testHelpers/index.js b/packages/test/page-object/src/globals/testHelpers/index.js
index ba4b6898a4..990843879f 100644
--- a/packages/test/page-object/src/globals/testHelpers/index.js
+++ b/packages/test/page-object/src/globals/testHelpers/index.js
@@ -15,6 +15,7 @@ import getAccessibilityNode from './getAccessibilityNode';
import getAccessibleName from './getAccessibleName';
import getAllTextContents from './getAllTextContents';
import iterateAsyncIterable from './iterateAsyncIterable';
+import preloadImages from './preloadImages';
import shareObservable from './shareObservable';
import sleep from './sleep';
import stringToArrayBuffer from './stringToArrayBuffer';
@@ -37,6 +38,7 @@ export {
getAccessibleName,
getAllTextContents,
iterateAsyncIterable,
+ preloadImages,
shareObservable,
sleep,
speech,
diff --git a/packages/test/page-object/src/globals/testHelpers/preloadImages.js b/packages/test/page-object/src/globals/testHelpers/preloadImages.js
new file mode 100644
index 0000000000..bb438f31f1
--- /dev/null
+++ b/packages/test/page-object/src/globals/testHelpers/preloadImages.js
@@ -0,0 +1,25 @@
+// Keep the images referenced, so the browser keeps them in its list of available images until they are rendered.
+const preloadedImages = new Set();
+
+/**
+ * Loads and decodes images before they are rendered.
+ *
+ * If an image is not available when its element is first painted, the browser repaints the area of the image later.
+ * Anti-aliased edges in that area (e.g. rounded corners and outlines) may come out slightly different from a full paint,
+ * and the screenshot may differ by a few pixels.
+ *
+ * @param {...string} urls - URLs of the images.
+ * @returns {Promise}
+ */
+export default async function preloadImages(...urls) {
+ await Promise.all(
+ urls.map(url => {
+ const image = new Image();
+
+ image.src = url;
+ preloadedImages.add(image);
+
+ return image.decode();
+ })
+ );
+}