diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx index be9b6d44b15..489c4d208ca 100644 --- a/src/containers/costume-tab.jsx +++ b/src/containers/costume-tab.jsx @@ -148,19 +148,27 @@ class CostumeTab extends React.Component { const blob = new Blob([item.asset.data], {type: item.asset.assetType.contentType}); downloadBlob(`${item.name}.${item.asset.dataFormat}`, blob); } - handleNewCostume (costume, fromCostumeLibrary, targetId) { + async handleNewCostume (costume, fromCostumeLibrary, targetId) { const costumes = Array.isArray(costume) ? costume : [costume]; - return Promise.all(costumes.map(c => { + // Costumes should be added one by one to ensure they are not received out of + // order by the VM. Parallel adding of costumes were causing out of order gifs + // for large gif costumes per #5875. Additionally, updated to async await + // for most up to date syntax. + const result = []; + for (const c of costumes) { if (fromCostumeLibrary) { - return this.props.vm.addCostumeFromLibrary(c.md5, c); + result.push(await this.props.vm.addCostumeFromLibrary(c.md5, c)); + } else { + // If targetId is falsy, VM should default it to editingTarget.id + // However, targetId should be provided to prevent #5876, + // if making new costume takes a while + result.push(await this.props.vm.addCostume(c.md5, c, targetId)); } - // If targetId is falsy, VM should default it to editingTarget.id - // However, targetId should be provided to prevent #5876, - // if making new costume takes a while - return this.props.vm.addCostume(c.md5, c, targetId); - })); + } + return result; } + handleNewBlankCostume () { const name = this.props.vm.editingTarget.isStage ? this.props.intl.formatMessage(messages.backdrop, {index: 1}) : diff --git a/test/fixtures/stones.gif b/test/fixtures/stones.gif new file mode 100644 index 00000000000..27be99e24ea Binary files /dev/null and b/test/fixtures/stones.gif differ diff --git a/test/integration/costumes.test.js b/test/integration/costumes.test.js index 3dcd4485456..a2932fa5847 100644 --- a/test/integration/costumes.test.js +++ b/test/integration/costumes.test.js @@ -182,6 +182,53 @@ describe('Working with costumes', () => { await expect(logs).toEqual([]); }); + test('Adding a costume with larger gif', async () => { + await loadUri(uri); + await clickText('Costumes'); + const el = await findByXpath('//button[@aria-label="Choose a Costume"]'); + await driver.actions().mouseMove(el) + .perform(); + await driver.sleep(500); // Wait for thermometer menu to come up + const input = await findByXpath('//input[@type="file"]'); + await input.sendKeys(path.resolve(__dirname, '../fixtures/stones.gif')); + + // Verify all frames (1-45) + for (let i = 1; i <= 45; i++) { + const frameName = i === 1 ? 'stones' : `stones${i}`; + await findByText(frameName, scope.costumesTab); + } + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); + + // test('Adding a large GIF as costumes (order verification)', async () => { + // await loadUri(uri); + // await clickText('Costumes'); + // const el = await findByXpath('//button[@aria-label="Choose a Costume"]'); + // await driver.actions().mouseMove(el).perform(); + // await driver.sleep(500); // Wait for menu to load + // const input = await findByXpath('//input[@type="file"]'); + // await input.sendKeys(path.resolve(__dirname, '../fixtures/stones.gif')); + + // // Retrieve costume elements in their UI order + // const costumeElements = await driver.findElements(By.css('.sprite-selector-item_sprite-name_iMqNV')); + // const costumeNames = []; + // for (const element of costumeElements) { + // const name = await element.getText(); + // costumeNames.push(name); + // } + // console.log('costume names found:', costumeNames); + + // const expectedNames = []; + // for (let i = 1; i <= 45; i++) { + // expectedNames.push(i === 1 ? 'stones' : `stones${i}`); + // } + // expect(costumeNames).toEqual(expectedNames); + + // const logs = await getLogs(); + // expect(logs).toEqual([]); + // }); + test('Adding a letter costume through the Letters filter in the library', async () => { await loadUri(uri); await driver.manage()