diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index b914de1e1..47d20b5a1 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -111,16 +111,45 @@ Feature: Downloading things from a website. does-not-exist does-not-exist - Scenario: Retrying with no failed downloads - When I open data/downloads/download.bin + Scenario: Retrying with count + When I run :download http://localhost:(port)/data/downloads/download.bin + And I run :download http://localhost:(port)/does-not-exist + And I wait for the error "Download error: * - server replied: NOT FOUND" + And I run :download-retry with count 2 + And I wait for the error "Download error: * - server replied: NOT FOUND" + Then the requests should be: + data/downloads/download.bin + does-not-exist + does-not-exist + + Scenario: Retrying with two failed downloads + When I run :download http://localhost:(port)/does-not-exist + And I run :download http://localhost:(port)/does-not-exist-2 + And I wait for the error "Download error: * - server replied: NOT FOUND" + And I wait for the error "Download error: * - server replied: NOT FOUND" + And I run :download-retry + And I wait for the error "Download error: * - server replied: NOT FOUND" + Then the requests should be: + does-not-exist + does-not-exist-2 + does-not-exist + + Scenario: Retrying a download which does not exist + When I run :download-retry with count 42 + Then the error "There's no download 42!" should be shown + + Scenario: Retrying a download which did not fail + When I run :download http://localhost:(port)/data/downloads/download.bin + And I wait until the download is finished + And I run :download-retry with count 1 + Then the error "Download 1 did not fail!" should be shown + + Scenario: Retrying a download with no failed ones + When I run :download http://localhost:(port)/data/downloads/download.bin And I wait until the download is finished And I run :download-retry Then the error "No failed downloads!" should be shown - Scenario: Retrying with no downloads - When I run :download-retry - Then the error "No failed downloads!" should be shown - ## Wrong invocations Scenario: :download with deprecated dest-old argument @@ -161,6 +190,10 @@ Feature: Downloading things from a website. And I run :download-cancel Then "cancelled" should be logged + Scenario: Cancelling with no download and no ID + When I run :download-cancel + Then the error "There's no download!" should be shown + Scenario: Cancelling a download which does not exist When I run :download-cancel with count 42 Then the error "There's no download 42!" should be shown @@ -192,6 +225,44 @@ Feature: Downloading things from a website. And I run :download-cancel Then no crash should happen + ## :download-remove / :download-clear + + Scenario: Removing a download + When I open data/downloads/download.bin + And I wait until the download is finished + And I run :download-remove + Then "Removed download *" should be logged + + Scenario: Removing a download which does not exist + When I run :download-remove with count 42 + Then the error "There's no download 42!" should be shown + + Scenario: Removing a download which is not done yet + When I run :download http://localhost:(port)/drip?numbytes=128&duration=5 + And I run :download-remove + Then the error "Download 1 is not done!" should be shown + + Scenario: Removing a download which is not done yet (with count) + When I run :download http://localhost:(port)/drip?numbytes=128&duration=5 + And I run :download-remove with count 1 + Then the error "Download 1 is not done!" should be shown + + Scenario: Removing all downloads via :download-remove + When I open data/downloads/download.bin + And I wait until the download is finished + And I open data/downloads/download2.bin + And I wait until the download is finished + And I run :download-remove --all + Then "Removed download *" should be logged + + Scenario: Removing all downloads via :download-clear + When I open data/downloads/download.bin + And I wait until the download is finished + And I open data/downloads/download2.bin + And I wait until the download is finished + And I run :download-clear + Then "Removed download *" should be logged + ## :download-delete Scenario: Deleting a download @@ -316,6 +387,62 @@ Feature: Downloading things from a website. And I open data/downloads/download2.bin Then the download prompt should be shown with "{downloaddir}/download2.bin" + # Overwriting files + + Scenario: Not overwriting an existing file + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/data/downloads/download.bin + And I wait until the download is finished + And I run :download http://localhost:(port)/data/downloads/download2.bin --dest download.bin + And I wait for "Entering mode KeyMode.yesno *" in the log + And I run :prompt-accept no + Then the downloaded file download.bin should contain 1 bytes + + Scenario: Overwriting an existing file + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/data/downloads/download.bin + And I wait until the download is finished + And I run :download http://localhost:(port)/data/downloads/download2.bin --dest download.bin + And I wait for "Entering mode KeyMode.yesno *" in the log + And I run :prompt-accept yes + And I wait until the download is finished + Then the downloaded file download.bin should contain 2 bytes + + @linux + Scenario: Not overwriting a special file + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/data/downloads/download.bin --dest fifo + And I wait for "Entering mode KeyMode.yesno *" in the log + And I run :prompt-accept no + Then the FIFO should still be a FIFO + + ## Redirects + + Scenario: Downloading with infinite redirect + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/redirect/12 --dest redirection + Then the error "Download error: Maximum redirection count reached!" should be shown + And "Deleted */redirection" should be logged + And the downloaded file redirection should not exist + + Scenario: Downloading with redirect to itself + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/custom/redirect-self + And I wait until the download is finished + Then the downloaded file redirect-self should exist + + Scenario: Downloading with absolute redirect + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/absolute-redirect/1 + And I wait until the download is finished + Then the downloaded file 1 should exist + + Scenario: Downloading with relative redirect + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/relative-redirect/1 + And I wait until the download is finished + Then the downloaded file 1 should exist + ## Other Scenario: Download without a content-size @@ -323,3 +450,39 @@ Feature: Downloading things from a website. When I run :download http://localhost:(port)/custom/content-size And I wait until the download is finished Then the downloaded file content-size should exist + + @posix + Scenario: Downloading to unwritable destination + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/data/downloads/download.bin --dest (tmpdir)/unwritable + Then the error "Download error: Permission denied" should be shown + + Scenario: Downloading 20MB file + When I set storage -> prompt-download-directory to false + And I run :download http://localhost:(port)/custom/twenty-mb + And I wait until the download is finished + Then the downloaded file twenty-mb should contain 20971520 bytes + + Scenario: Downloading 20MB file with late prompt confirmation + When I set storage -> prompt-download-directory to true + And I run :download http://localhost:(port)/custom/twenty-mb + And I wait 1s + And I run :prompt-accept + And I wait until the download is finished + Then the downloaded file twenty-mb should contain 20971520 bytes + + Scenario: Downloading invalid URL + When I set storage -> prompt-download-directory to false + And I set general -> auto-search to false + And I run :download foo! + Then the error "Invalid URL" should be shown + + Scenario: Downloading via pdfjs + Given pdfjs is available + When I set storage -> prompt-download-directory to false + And I set content -> enable-pdfjs to true + And I open data/misc/test.pdf + And I wait for the javascript message "PDF * [*] (PDF.js: *)" + And I run :click-element id download + And I wait until the download is finished + Then the downloaded file test.pdf should exist diff --git a/tests/end2end/features/test_downloads_bdd.py b/tests/end2end/features/test_downloads_bdd.py index c7aab9b56..5990d9b3d 100644 --- a/tests/end2end/features/test_downloads_bdd.py +++ b/tests/end2end/features/test_downloads_bdd.py @@ -41,6 +41,13 @@ def temporary_download_dir(quteproc, tmpdir): quteproc.set_setting('storage', 'remember-download-directory', 'false') quteproc.set_setting('storage', 'download-directory', str(tmpdir)) (tmpdir / 'subdir').ensure(dir=True) + try: + os.mkfifo(str(tmpdir / 'fifo')) + except AttributeError: + pass + unwritable = tmpdir / 'unwritable' + unwritable.ensure(dir=True) + unwritable.chmod(0) @bdd.given("I clean old downloads") @@ -84,6 +91,13 @@ def download_should_exist(filename, tmpdir): assert path.check() +@bdd.then(bdd.parsers.parse("The downloaded file {filename} should contain " + "{size} bytes")) +def download_size(filename, size, tmpdir): + path = tmpdir / filename + assert path.size() == int(size) + + @bdd.then(bdd.parsers.parse('The download prompt should be shown with ' '"{path}"')) def download_prompt(tmpdir, quteproc, path): @@ -108,3 +122,13 @@ def download_open(quteproc): def download_open_with_prompt(quteproc): cmd = '{} -c pass'.format(shlex.quote(sys.executable)) quteproc.send_cmd(':prompt-open-download {}'.format(cmd)) + + +@bdd.when(bdd.parsers.parse("I delete the downloaded file {filename}")) +def delete_file(tmpdir, filename): + (tmpdir / filename).remove() + + +@bdd.then("the FIFO should still be a FIFO") +def fifo_should_be_fifo(tmpdir): + assert tmpdir.exists() and not os.path.isfile(str(tmpdir / 'fifo')) diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py index 7f789cc03..313884118 100644 --- a/tests/end2end/fixtures/webserver.py +++ b/tests/end2end/fixtures/webserver.py @@ -66,13 +66,21 @@ class Request(testprocess.Line): path_to_statuses = { '/favicon.ico': [http.client.NOT_FOUND], '/does-not-exist': [http.client.NOT_FOUND], - '/custom/redirect-later': [http.client.FOUND], + '/does-not-exist-2': [http.client.NOT_FOUND], '/basic-auth/user/password': [http.client.UNAUTHORIZED, http.client.OK], + '/custom/redirect-later': [http.client.FOUND], + '/custom/redirect-self': [http.client.FOUND], '/redirect-to': [http.client.FOUND], '/status/404': [http.client.NOT_FOUND], '/cookies/set': [http.client.FOUND], } + for i in range(15): + path_to_statuses['/redirect/{}'.format(i)] = [http.client.FOUND] + path_to_statuses['/relative-redirect/{}'.format(i)] = [ + http.client.FOUND] + path_to_statuses['/absolute-redirect/{}'.format(i)] = [ + http.client.FOUND] default_statuses = [http.client.OK, http.client.NOT_MODIFIED] sanitized = QUrl('http://localhost' + self.path).path() # Remove ?foo diff --git a/tests/end2end/fixtures/webserver_sub.py b/tests/end2end/fixtures/webserver_sub.py index 7014a3220..bd5e92f85 100644 --- a/tests/end2end/fixtures/webserver_sub.py +++ b/tests/end2end/fixtures/webserver_sub.py @@ -100,6 +100,27 @@ def content_size(): return response +@app.route('/custom/twenty-mb') +def twenty_mb(): + """Send 20MB of data.""" + + def generate_bytes(): + yield b'*' * 20 * 1024 * 1024 + + response = flask.Response(generate_bytes(), headers={ + "Content-Type": "application/octet-stream", + "Content-Length": str(20 * 1024 * 1024), + }) + response.status_code = 200 + return response + + +@app.route('/custom/redirect-self') +def redirect_self(): + """302 Redirects to itself.""" + return app.make_response(flask.redirect(flask.url_for('redirect_self'))) + + @app.after_request def log_request(response): """Log a webserver request."""