From c51c66805d4d823e0182473ecf231fa7af7a4fc0 Mon Sep 17 00:00:00 2001 From: Ayushi Gorai Date: Tue, 28 Feb 2023 06:56:00 +0000 Subject: [PATCH] [FIX] base_import: prevent the list out of range error When the user uploads a file the length of the list will return 1, but when it tries to retrieve a file which is not in the list or wants to upload another file while the process is ongoing it will throw a IndexError. After this commit, the race condition is satisfied i.e. now the user will not able to upload the file while the uploading is still going on. sentry-3938949468 closes odoo/odoo#120012 X-original-commit: 482e0034e1a5882ea1e0bc4ddb56d8ef5f5a1a92 Signed-off-by: Samuel Degueldre --- .../static/src/core/file_input/file_input.js | 8 +++++- .../static/src/core/file_input/file_input.xml | 3 ++- .../web/static/tests/core/file_input_tests.js | 27 ++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/core/file_input/file_input.js b/addons/web/static/src/core/file_input/file_input.js index cc18401c73c..70159beecd7 100644 --- a/addons/web/static/src/core/file_input/file_input.js +++ b/addons/web/static/src/core/file_input/file_input.js @@ -2,7 +2,7 @@ import { useService } from "@web/core/utils/hooks"; -import { Component, onMounted, useRef } from "@odoo/owl"; +import { Component, onMounted, useRef, useState } from "@odoo/owl"; /** * Custom file input @@ -26,6 +26,10 @@ export class FileInput extends Component { setup() { this.http = useService("http"); this.fileInputRef = useRef("file-input"); + this.state = useState({ + // Disables upload button if currently uploading. + isDisable: false, + }); onMounted(() => { if (this.props.autoOpen) { @@ -70,12 +74,14 @@ export class FileInput extends Component { * - resId: the id of the resModel target instance */ async onFileInputChange() { + this.state.isDisable = true; const parsedFileData = await this.uploadFiles(this.httpParams); // When calling onUpload, also pass the files to allow to get data like their names this.props.onUpload(parsedFileData, this.fileInputRef.el ? this.fileInputRef.el.files : []); // Because the input would not trigger this method if the same file name is uploaded, // we must clear the value after handling the upload this.fileInputRef.el.value = null; + this.state.isDisable = false; } /** diff --git a/addons/web/static/src/core/file_input/file_input.xml b/addons/web/static/src/core/file_input/file_input.xml index e8322906704..3ee96fe923a 100644 --- a/addons/web/static/src/core/file_input/file_input.xml +++ b/addons/web/static/src/core/file_input/file_input.xml @@ -2,7 +2,7 @@ - + @@ -14,6 +14,7 @@ t-att-multiple="props.multiUpload" t-att-accept="props.acceptedFileExtensions" t-ref="file-input" + t-att-disabled="state.isDisable" t-on-change="onFileInputChange" /> diff --git a/addons/web/static/tests/core/file_input_tests.js b/addons/web/static/tests/core/file_input_tests.js index a784adecaa4..d346d424d2f 100644 --- a/addons/web/static/tests/core/file_input_tests.js +++ b/addons/web/static/tests/core/file_input_tests.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { makeTestEnv } from "@web/../tests/helpers/mock_env"; -import { editInput, getFixture, mount, patchWithCleanup, triggerEvent } from "@web/../tests/helpers/utils"; +import { editInput, getFixture, mount, patchWithCleanup, triggerEvent, makeDeferred, nextTick } from "@web/../tests/helpers/utils"; import { FileInput } from "@web/core/file_input/file_input"; import { registry } from "@web/core/registry"; @@ -138,4 +138,29 @@ QUnit.module("Components", ({ beforeEach }) => { await editInput(target, ".o_file_input input", file); assert.verifySteps(["fake_file.txt"], "file has been uploaded a second time"); }); + + QUnit.test("Upload button is disabled if attachment upload is not finished", async (assert) => { + assert.expect(2); + + const uploadedPromise = makeDeferred(); + await createFileInput({ + mockPost: async (route, params) => { + if (route === "/web/binary/upload_attachment") { + await uploadedPromise; + } + return "[]"; + }, + props: {}, + }); + //enable button + const input = target.querySelector(".o_file_input input"); + await triggerEvent(input, null, "change", {}, { skipVisibilityCheck: true }); + + //disable button + assert.ok(input.disabled, "the upload button should be disabled on upload"); + + uploadedPromise.resolve(); + await nextTick(); + assert.notOk(input.disabled, "the upload button should be enabled for upload"); + }); });