]> source.dussan.org Git - vaadin-framework.git/commitdiff
Ensure Upload is properly reset after an upload is interrupted (#10522)
authorAnna Koskinen <Ansku@users.noreply.github.com>
Thu, 11 Jan 2018 14:20:43 +0000 (16:20 +0200)
committerTeemu Suo-Anttila <tsuoanttila@users.noreply.github.com>
Thu, 11 Jan 2018 14:20:42 +0000 (16:20 +0200)
Fixes #9635

client/src/main/java/com/vaadin/client/ui/VUpload.java
uitest/src/main/java/com/vaadin/tests/components/upload/InterruptUpload.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/upload/InterruptUploadTest.java [new file with mode: 0644]

index 16d62ccf4268027c468a6d00ba123f06d8ec34b6..5f191162c2e1aed2cba3c8a6ac1e6fa376e46f53 100644 (file)
@@ -222,6 +222,7 @@ public class VUpload extends SimplePanel {
              * file. A new target frame is created later."
              */
             cleanTargetFrame();
+            rebuildPanel();
             submitted = false;
         }
     }
diff --git a/uitest/src/main/java/com/vaadin/tests/components/upload/InterruptUpload.java b/uitest/src/main/java/com/vaadin/tests/components/upload/InterruptUpload.java
new file mode 100644 (file)
index 0000000..8d95b35
--- /dev/null
@@ -0,0 +1,208 @@
+package com.vaadin.tests.components.upload;
+
+import java.io.OutputStream;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.ProgressBar;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.Upload;
+import com.vaadin.ui.Upload.FailedEvent;
+import com.vaadin.ui.Upload.FinishedEvent;
+import com.vaadin.ui.Upload.Receiver;
+import com.vaadin.ui.Upload.StartedEvent;
+import com.vaadin.ui.Upload.SucceededEvent;
+import com.vaadin.ui.Window;
+
+public class InterruptUpload extends AbstractTestUI {
+
+    private Upload sample;
+    private UploadInfoWindow uploadInfoWindow;
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        LineBreakCounter lineBreakCounter = new LineBreakCounter();
+        lineBreakCounter.setSlow(true);
+
+        sample = new Upload(null, lineBreakCounter);
+        sample.setImmediateMode(true);
+        sample.setButtonCaption("Upload File");
+
+        uploadInfoWindow = new UploadInfoWindow(sample, lineBreakCounter);
+
+        sample.addStartedListener(event -> {
+            if (uploadInfoWindow.getParent() == null) {
+                UI.getCurrent().addWindow(uploadInfoWindow);
+            }
+            uploadInfoWindow.setClosable(false);
+        });
+        sample.addFinishedListener(event -> uploadInfoWindow.setClosable(true));
+
+        addComponent(sample);
+    }
+
+    private static class UploadInfoWindow extends Window
+            implements Upload.StartedListener, Upload.ProgressListener,
+            Upload.FailedListener, Upload.SucceededListener,
+            Upload.FinishedListener {
+        private final Label state = new Label();
+        private final Label result = new Label();
+        private final Label fileName = new Label();
+        private final Label textualProgress = new Label();
+
+        private final ProgressBar progressBar = new ProgressBar();
+        private final Button cancelButton;
+        private final LineBreakCounter counter;
+
+        private UploadInfoWindow(final Upload upload,
+                final LineBreakCounter lineBreakCounter) {
+            super("Status");
+            counter = lineBreakCounter;
+
+            addStyleName("upload-info");
+
+            setResizable(false);
+            setDraggable(false);
+
+            final FormLayout uploadInfoLayout = new FormLayout();
+            setContent(uploadInfoLayout);
+            uploadInfoLayout.setMargin(true);
+
+            final HorizontalLayout stateLayout = new HorizontalLayout();
+            stateLayout.setSpacing(true);
+            stateLayout.addComponent(state);
+
+            cancelButton = new Button("Cancel");
+            cancelButton.addClickListener(event -> upload.interruptUpload());
+            cancelButton.setVisible(false);
+            cancelButton.setStyleName("small");
+            stateLayout.addComponent(cancelButton);
+
+            stateLayout.setCaption("Current state");
+            state.setValue("Idle");
+            uploadInfoLayout.addComponent(stateLayout);
+
+            fileName.setCaption("File name");
+            uploadInfoLayout.addComponent(fileName);
+
+            result.setCaption("Line breaks counted");
+            uploadInfoLayout.addComponent(result);
+
+            progressBar.setCaption("Progress");
+            progressBar.setVisible(false);
+            uploadInfoLayout.addComponent(progressBar);
+
+            textualProgress.setVisible(false);
+            uploadInfoLayout.addComponent(textualProgress);
+
+            upload.addStartedListener(this);
+            upload.addProgressListener(this);
+            upload.addFailedListener(this);
+            upload.addSucceededListener(this);
+            upload.addFinishedListener(this);
+
+        }
+
+        @Override
+        public void uploadFinished(final FinishedEvent event) {
+            state.setValue("Idle");
+            progressBar.setVisible(false);
+            textualProgress.setVisible(false);
+            cancelButton.setVisible(false);
+            UI.getCurrent().setPollInterval(-1);
+        }
+
+        @Override
+        public void uploadStarted(final StartedEvent event) {
+            // this method gets called immediately after upload is started
+            progressBar.setValue(0f);
+            progressBar.setVisible(true);
+            UI.getCurrent().setPollInterval(500);
+            textualProgress.setVisible(true);
+            // updates to client
+            state.setValue("Uploading");
+            fileName.setValue(event.getFilename());
+
+            cancelButton.setVisible(true);
+        }
+
+        @Override
+        public void updateProgress(final long readBytes,
+                final long contentLength) {
+            // this method gets called several times during the update
+            progressBar.setValue(readBytes / (float) contentLength);
+            textualProgress.setValue(
+                    "Processed " + readBytes + " bytes of " + contentLength);
+            result.setValue(counter.getLineBreakCount() + " (counting...)");
+        }
+
+        @Override
+        public void uploadSucceeded(final SucceededEvent event) {
+            result.setValue(counter.getLineBreakCount() + " (total)");
+        }
+
+        @Override
+        public void uploadFailed(final FailedEvent event) {
+            result.setValue(
+                    counter.getLineBreakCount() + " (counting interrupted at "
+                            + Math.round(100 * progressBar.getValue()) + "%)");
+        }
+    }
+
+    private static class LineBreakCounter implements Receiver {
+        private int counter;
+        private int total;
+        private boolean sleep;
+
+        /**
+         * return an OutputStream that simply counts lineends
+         */
+        @Override
+        public OutputStream receiveUpload(final String filename,
+                final String MIMEType) {
+            counter = 0;
+            total = 0;
+            return new OutputStream() {
+                private static final int searchedByte = '\n';
+
+                @Override
+                public void write(final int b) {
+                    total++;
+                    if (b == searchedByte) {
+                        counter++;
+                    }
+                    if (sleep && total % 1000 == 0) {
+                        try {
+                            Thread.sleep(100);
+                        } catch (final InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            };
+        }
+
+        private int getLineBreakCount() {
+            return counter;
+        }
+
+        private void setSlow(boolean value) {
+            sleep = value;
+        }
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 9635;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Interrupting an upload shouldn't prevent uploading that same file immediately afterwards.";
+    }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/upload/InterruptUploadTest.java b/uitest/src/test/java/com/vaadin/tests/components/upload/InterruptUploadTest.java
new file mode 100644 (file)
index 0000000..1df1882
--- /dev/null
@@ -0,0 +1,112 @@
+package com.vaadin.tests.components.upload;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.internal.WrapsElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.LocalFileDetector;
+import org.openqa.selenium.remote.RemoteWebElement;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.LabelElement;
+import com.vaadin.testbench.elements.WindowElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+import com.vaadin.tests.util.LoremIpsum;
+
+public class InterruptUploadTest extends MultiBrowserTest {
+
+    @Override
+    public List<DesiredCapabilities> getBrowsersToTest() {
+        // PhantomJS fails to upload files for unknown reasons
+        return getBrowsersExcludingPhantomJS();
+    }
+
+    @Test
+    public void testInterruptUpload() throws Exception {
+        openTestURL();
+
+        File tempFile = createTempFile();
+        fillPathToUploadInput(tempFile.getPath());
+
+        waitForElementPresent(By.className("v-window"));
+
+        $(ButtonElement.class).caption("Cancel").first().click();
+
+        String expected = " (counting interrupted at ";
+        String actual = $(LabelElement.class).caption("Line breaks counted")
+                .first().getText();
+        assertTrue("Line break count note does not match expected (was: "
+                + actual + ")", actual.contains(expected));
+
+        $(WindowElement.class).first().close();
+        waitForElementNotPresent(By.className("v-window"));
+
+        tempFile = createTempFile();
+        fillPathToUploadInput(tempFile.getPath());
+
+        waitForElementPresent(By.className("v-window"));
+        $(ButtonElement.class).caption("Cancel").first().click();
+    }
+
+    /**
+     * @return The generated temp file handle
+     * @throws IOException
+     */
+    private File createTempFile() throws IOException {
+        File tempFile = File.createTempFile("TestFileUpload", ".txt");
+        BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
+        writer.write(getTempFileContents());
+        writer.close();
+        tempFile.deleteOnExit();
+        return tempFile;
+    }
+
+    private String getTempFileContents() {
+        StringBuilder sb = new StringBuilder("This is a big test file!");
+        for (int i = 0; i < 70; ++i) {
+            sb.append("\n");
+            sb.append(LoremIpsum.get());
+        }
+        return sb.toString();
+    }
+
+    private void fillPathToUploadInput(String tempFileName) throws Exception {
+        // create a valid path in upload input element. Instead of selecting a
+        // file by some file browsing dialog, we use the local path directly.
+        WebElement input = getInput();
+        setLocalFileDetector(input);
+        input.sendKeys(tempFileName);
+    }
+
+    private WebElement getInput() {
+        return getDriver().findElement(By.className("gwt-FileUpload"));
+    }
+
+    private void setLocalFileDetector(WebElement element) throws Exception {
+        if (getRunLocallyBrowser() != null) {
+            return;
+        }
+
+        if (element instanceof WrapsElement) {
+            element = ((WrapsElement) element).getWrappedElement();
+        }
+        if (element instanceof RemoteWebElement) {
+            ((RemoteWebElement) element)
+                    .setFileDetector(new LocalFileDetector());
+        } else {
+            throw new IllegalArgumentException(
+                    "Expected argument of type RemoteWebElement, received "
+                            + element.getClass().getName());
+        }
+    }
+
+}