From 34072d2f531185c35f1c1c201788045e148262ae Mon Sep 17 00:00:00 2001
From: adroitwhiz <adroitwhiz@protonmail.com>
Date: Sun, 8 Mar 2020 08:12:40 -0400
Subject: [PATCH] Fix file input in tests

---
 test/helper/page-util.js          | 53 +++++++++++++++++++++++++++++++
 test/integration/cpu-render.html  | 43 +++++++------------------
 test/integration/index.html       | 31 ++----------------
 test/integration/pick-tests.js    |  9 ++++--
 test/integration/scratch-tests.js |  8 +++--
 5 files changed, 79 insertions(+), 65 deletions(-)
 create mode 100644 test/helper/page-util.js

diff --git a/test/helper/page-util.js b/test/helper/page-util.js
new file mode 100644
index 00000000..0648e1bf
--- /dev/null
+++ b/test/helper/page-util.js
@@ -0,0 +1,53 @@
+/* global window, VirtualMachine, ScratchStorage, ScratchSVGRenderer */
+/* eslint-env browser */
+
+// Wait for all SVG skins to be loaded.
+// TODO: this is extremely janky and should be removed once vm.loadProject waits for SVG skins to load
+window.waitForSVGSkinLoad = renderer => new Promise(resolve => {
+    // eslint-disable-next-line prefer-const
+    let interval;
+
+    const waitInner = () => {
+        let numSVGSkins = 0;
+        let numLoadedSVGSkins = 0;
+        for (const skin of renderer._allSkins) {
+            if (skin.constructor.name !== 'SVGSkin') continue;
+            numSVGSkins++;
+            if (skin._svgRenderer.loaded) numLoadedSVGSkins++;
+        }
+
+        if (numSVGSkins === numLoadedSVGSkins) {
+            clearInterval(interval);
+            resolve();
+        }
+    };
+
+    interval = setInterval(waitInner, 1);
+});
+
+window.loadFileInputIntoVM = (fileInput, vm, render) => {
+    const reader = new FileReader();
+    return new Promise(resolve => {
+        reader.onload = () => {
+            vm.start();
+            vm.loadProject(reader.result)
+                .then(() => window.waitForSVGSkinLoad(render))
+                .then(() => {
+                    resolve();
+                });
+        };
+        reader.readAsArrayBuffer(fileInput.files[0]);
+    });
+};
+
+window.initVM = render => {
+    const vm = new VirtualMachine();
+    const storage = new ScratchStorage();
+
+    vm.attachStorage(storage);
+    vm.attachRenderer(render);
+    vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
+    vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
+
+    return vm;
+};
diff --git a/test/integration/cpu-render.html b/test/integration/cpu-render.html
index d6d36308..26a79c3f 100644
--- a/test/integration/cpu-render.html
+++ b/test/integration/cpu-render.html
@@ -2,6 +2,7 @@
     <script src="../../node_modules/scratch-vm/dist/web/scratch-vm.js"></script>
     <script src="../../node_modules/scratch-storage/dist/web/scratch-storage.js"></script>
     <script src="../../node_modules/scratch-svg-renderer/dist/web/scratch-svg-renderer.js"></script>
+    <script src="../helper/page-util.js"></script>
     <!-- note: this uses the BUILT version of scratch-render!  make sure to npm run build -->
     <script src="../../dist/web/scratch-render.js"></script>
 
@@ -17,38 +18,18 @@
         window.devicePixelRatio = 1;
         const gpuCanvas = document.getElementById('test');
         var render = new ScratchRender(gpuCanvas);
-        var vm = new VirtualMachine();
-        var storage = new ScratchStorage();
+        var vm = initVM(render);
 
-        vm.attachStorage(storage);
-        vm.attachRenderer(render);
-        vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
-        vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
-
-        document.getElementById('file').addEventListener('click', e => {
-            document.body.removeChild(document.getElementById('loaded'));
-        });
-
-        document.getElementById('file').addEventListener('change', e => {
-            const reader = new FileReader();
-            const thisFileInput = e.target;
-            reader.onload = () => {
-                vm.start();
-                vm.loadProject(reader.result)
-                    .then(() => {
-                        // we add a `#loaded` div to our document, the integration suite
-                        // waits for that element to show up to assume the vm is ready
-                        // to play!
-                        const div = document.createElement('div');
-                        div.id='loaded';
-                        document.body.appendChild(div);
-                        vm.greenFlag();
-                        setTimeout(() => {
-                            renderCpu();
-                        }, 1000);
-                    });
-            };
-            reader.readAsArrayBuffer(thisFileInput.files[0]);
+        const fileInput = document.getElementById('file');
+        const loadFile = loadFileInputIntoVM.bind(null, fileInput, vm, render);
+        fileInput.addEventListener('change', e => {
+            loadFile()
+                .then(() => {
+                    vm.greenFlag();
+                    setTimeout(() => {
+                        renderCpu();
+                    }, 1000);
+                });
         });
 
         const cpuCanvas = document.getElementById('cpu');
diff --git a/test/integration/index.html b/test/integration/index.html
index e3d8dd83..114fa5b9 100644
--- a/test/integration/index.html
+++ b/test/integration/index.html
@@ -2,6 +2,7 @@
     <script src="../../node_modules/scratch-vm/dist/web/scratch-vm.js"></script>
     <script src="../../node_modules/scratch-storage/dist/web/scratch-storage.js"></script>
     <script src="../../node_modules/scratch-svg-renderer/dist/web/scratch-svg-renderer.js"></script>
+    <script src="../helper/page-util.js"></script>
     <!-- note: this uses the BUILT version of scratch-render!  make sure to npm run build -->
     <script src="../../dist/web/scratch-render.js"></script>
 
@@ -15,39 +16,13 @@
 
         var canvas = document.getElementById('test');
         var render = new ScratchRender(canvas);
-        var vm = new VirtualMachine();
-        var storage = new ScratchStorage();
+        var vm = initVM(render);
         var mockMouse = data => vm.runtime.postIOData('mouse', {
             canvasWidth: canvas.width,
             canvasHeight: canvas.height,
             ...data,
         });
 
-        vm.attachStorage(storage);
-        vm.attachRenderer(render);
-        vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
-        vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
-
-        document.getElementById('file').addEventListener('click', e => {
-            document.body.removeChild(document.getElementById('loaded'));
-        });
-
-        document.getElementById('file').addEventListener('change', e => {
-            const reader = new FileReader();
-            const thisFileInput = e.target;
-            reader.onload = () => {
-                vm.start();
-                vm.loadProject(reader.result)
-                    .then(() => {
-                        // we add a `#loaded` div to our document, the integration suite
-                        // waits for that element to show up to assume the vm is ready
-                        // to play!
-                        const div = document.createElement('div');
-                        div.id='loaded';
-                        document.body.appendChild(div);
-                    });
-            };
-            reader.readAsArrayBuffer(thisFileInput.files[0]);
-        });
+        const loadFile = loadFileInputIntoVM.bind(null, document.getElementById('file'), vm, render);
     </script>
 </body>
diff --git a/test/integration/pick-tests.js b/test/integration/pick-tests.js
index a767ec40..a36ee150 100644
--- a/test/integration/pick-tests.js
+++ b/test/integration/pick-tests.js
@@ -13,9 +13,12 @@ const runFile = async (file, action, page, script) => {
     await page.goto(`file://${indexHTML}`);
     const fileInput = await page.$('#file');
     await fileInput.uploadFile(testDir(file));
-    // the index.html handler for file input will add a #loaded element when it
-    // finishes.
-    await page.waitForSelector('#loaded');
+
+    await page.evaluate(() =>
+        // `loadFile` is defined on the page itself.
+        // eslint-disable-next-line no-undef
+        loadFile()
+    );
     return page.evaluate(`(function () {return (${script})(${action});})()`);
 };
 
diff --git a/test/integration/scratch-tests.js b/test/integration/scratch-tests.js
index d6cfebfd..9d3188a3 100644
--- a/test/integration/scratch-tests.js
+++ b/test/integration/scratch-tests.js
@@ -14,9 +14,11 @@ const testFile = (file, page) => test(file, async t => {
     await page.goto(`file://${indexHTML}`);
     const fileInput = await page.$('#file');
     await fileInput.uploadFile(testDir(file));
-    // the index.html handler for file input will add a #loaded element when it
-    // finishes.
-    await page.waitForSelector('#loaded');
+    await page.evaluate(() =>
+        // `loadFile` is defined on the page itself.
+        // eslint-disable-next-line no-undef
+        loadFile()
+    );
     const says = await page.evaluate(() => {
         // This function is run INSIDE the integration chrome browser via some
         // injection and .toString() magic.  We can return some "simple data"