Merge branch 'ability_to_cancel_attaching_file' into 'master'
Add an ability to cancel attaching file and redesign attaching files UI Closes #15611, #24270, and #28905 See merge request !9431
This commit is contained in:
commit
83aa0235ab
7 changed files with 330 additions and 117 deletions
|
@ -5,104 +5,154 @@ require('./preview_markdown');
|
|||
|
||||
window.DropzoneInput = (function() {
|
||||
function DropzoneInput(form) {
|
||||
var $mdArea, alertAttr, alertClass, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divAlert, divHover, divSpinner, dropzone, form_dropzone, form_textarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, max_file_size, pasteText, uploads_path, showError, showSpinner, uploadFile, uploadProgress;
|
||||
var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile;
|
||||
Dropzone.autoDiscover = false;
|
||||
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert";
|
||||
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\"";
|
||||
divHover = "<div class=\"div-dropzone-hover\"></div>";
|
||||
divSpinner = "<div class=\"div-dropzone-spinner\"></div>";
|
||||
divAlert = "<div class=\"" + alertClass + "\"></div>";
|
||||
iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>";
|
||||
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>";
|
||||
uploadProgress = $("<div class=\"div-dropzone-progress\"></div>");
|
||||
btnAlert = "<button type=\"button\"" + alertAttr + ">×</button>";
|
||||
uploads_path = window.uploads_path || null;
|
||||
max_file_size = gon.max_file_size || 10;
|
||||
form_textarea = $(form).find(".js-gfm-input");
|
||||
form_textarea.wrap("<div class=\"div-dropzone\"></div>");
|
||||
form_textarea.on('paste', (function(_this) {
|
||||
divHover = '<div class="div-dropzone-hover"></div>';
|
||||
iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
|
||||
$attachButton = form.find('.button-attach-file');
|
||||
$attachingFileMessage = form.find('.attaching-file-message');
|
||||
$cancelButton = form.find('.button-cancel-uploading-files');
|
||||
$retryLink = form.find('.retry-uploading-link');
|
||||
$uploadProgress = form.find('.uploading-progress');
|
||||
$uploadingErrorContainer = form.find('.uploading-error-container');
|
||||
$uploadingErrorMessage = form.find('.uploading-error-message');
|
||||
$uploadingProgressContainer = form.find('.uploading-progress-container');
|
||||
uploadsPath = window.uploads_path || null;
|
||||
maxFileSize = gon.max_file_size || 10;
|
||||
formTextarea = form.find('.js-gfm-input');
|
||||
formTextarea.wrap('<div class="div-dropzone"></div>');
|
||||
formTextarea.on('paste', (function(_this) {
|
||||
return function(event) {
|
||||
return handlePaste(event);
|
||||
};
|
||||
})(this));
|
||||
$mdArea = $(form_textarea).closest('.md-area');
|
||||
$(form).setupMarkdownPreview();
|
||||
form_dropzone = $(form).find('.div-dropzone');
|
||||
form_dropzone.parent().addClass("div-dropzone-wrapper");
|
||||
form_dropzone.append(divHover);
|
||||
form_dropzone.find(".div-dropzone-hover").append(iconPaperclip);
|
||||
form_dropzone.append(divSpinner);
|
||||
form_dropzone.find(".div-dropzone-spinner").append(iconSpinner);
|
||||
form_dropzone.find(".div-dropzone-spinner").append(uploadProgress);
|
||||
form_dropzone.find(".div-dropzone-spinner").css({
|
||||
"opacity": 0,
|
||||
"display": "none"
|
||||
});
|
||||
|
||||
if (!uploads_path) return;
|
||||
// Add dropzone area to the form.
|
||||
$mdArea = formTextarea.closest('.md-area');
|
||||
form.setupMarkdownPreview();
|
||||
$formDropzone = form.find('.div-dropzone');
|
||||
$formDropzone.parent().addClass('div-dropzone-wrapper');
|
||||
$formDropzone.append(divHover);
|
||||
$formDropzone.find('.div-dropzone-hover').append(iconPaperclip);
|
||||
|
||||
dropzone = form_dropzone.dropzone({
|
||||
url: uploads_path,
|
||||
dictDefaultMessage: "",
|
||||
if (!uploadsPath) return;
|
||||
|
||||
dropzone = $formDropzone.dropzone({
|
||||
url: uploadsPath,
|
||||
dictDefaultMessage: '',
|
||||
clickable: true,
|
||||
paramName: "file",
|
||||
maxFilesize: max_file_size,
|
||||
paramName: 'file',
|
||||
maxFilesize: maxFileSize,
|
||||
uploadMultiple: false,
|
||||
headers: {
|
||||
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
previewContainer: false,
|
||||
processing: function() {
|
||||
return $(".div-dropzone-alert").alert("close");
|
||||
return $('.div-dropzone-alert').alert('close');
|
||||
},
|
||||
dragover: function() {
|
||||
$mdArea.addClass('is-dropzone-hover');
|
||||
form.find(".div-dropzone-hover").css("opacity", 0.7);
|
||||
form.find('.div-dropzone-hover').css('opacity', 0.7);
|
||||
},
|
||||
dragleave: function() {
|
||||
$mdArea.removeClass('is-dropzone-hover');
|
||||
form.find(".div-dropzone-hover").css("opacity", 0);
|
||||
form.find('.div-dropzone-hover').css('opacity', 0);
|
||||
},
|
||||
drop: function() {
|
||||
$mdArea.removeClass('is-dropzone-hover');
|
||||
form.find(".div-dropzone-hover").css("opacity", 0);
|
||||
form_textarea.focus();
|
||||
form.find('.div-dropzone-hover').css('opacity', 0);
|
||||
formTextarea.focus();
|
||||
},
|
||||
success: function(header, response) {
|
||||
const processingFileCount = this.getQueuedFiles().length + this.getUploadingFiles().length;
|
||||
const shouldPad = processingFileCount >= 1;
|
||||
|
||||
pasteText(response.link.markdown, shouldPad);
|
||||
// Show 'Attach a file' link only when all files have been uploaded.
|
||||
if (!processingFileCount) $attachButton.removeClass('hide');
|
||||
},
|
||||
error: function(temp) {
|
||||
var checkIfMsgExists, errorAlert;
|
||||
errorAlert = $(form).find('.error-alert');
|
||||
checkIfMsgExists = errorAlert.children().length;
|
||||
if (checkIfMsgExists === 0) {
|
||||
errorAlert.append(divAlert);
|
||||
$(".div-dropzone-alert").append(btnAlert + "Attaching the file failed.");
|
||||
}
|
||||
error: function(file, errorMessage = 'Attaching the file failed.', xhr) {
|
||||
// If 'error' event is fired by dropzone, the second parameter is error message.
|
||||
// If the 'errorMessage' parameter is empty, the default error message is set.
|
||||
// If the 'error' event is fired by backend (xhr) error response, the third parameter is
|
||||
// xhr object (xhr.responseText is error message).
|
||||
// On error we hide the 'Attach' and 'Cancel' buttons
|
||||
// and show an error.
|
||||
|
||||
// If there's xhr error message, let's show it instead of dropzone's one.
|
||||
const message = xhr ? xhr.responseText : errorMessage;
|
||||
|
||||
$uploadingErrorContainer.removeClass('hide');
|
||||
$uploadingErrorMessage.html(message);
|
||||
$attachButton.addClass('hide');
|
||||
$cancelButton.addClass('hide');
|
||||
},
|
||||
totaluploadprogress: function(totalUploadProgress) {
|
||||
uploadProgress.text(Math.round(totalUploadProgress) + "%");
|
||||
updateAttachingMessage(this.files, $attachingFileMessage);
|
||||
$uploadProgress.text(Math.round(totalUploadProgress) + '%');
|
||||
},
|
||||
sending: function() {
|
||||
form_dropzone.find(".div-dropzone-spinner").css({
|
||||
"opacity": 0.7,
|
||||
"display": "inherit"
|
||||
});
|
||||
sending: function(file) {
|
||||
// DOM elements already exist.
|
||||
// Instead of dynamically generating them,
|
||||
// we just either hide or show them.
|
||||
$attachButton.addClass('hide');
|
||||
$uploadingErrorContainer.addClass('hide');
|
||||
$uploadingProgressContainer.removeClass('hide');
|
||||
$cancelButton.removeClass('hide');
|
||||
},
|
||||
removedfile: function() {
|
||||
$attachButton.removeClass('hide');
|
||||
$cancelButton.addClass('hide');
|
||||
$uploadingProgressContainer.addClass('hide');
|
||||
$uploadingErrorContainer.addClass('hide');
|
||||
},
|
||||
queuecomplete: function() {
|
||||
uploadProgress.text("");
|
||||
$(".dz-preview").remove();
|
||||
$(".markdown-area").trigger("input");
|
||||
$(".div-dropzone-spinner").css({
|
||||
"opacity": 0,
|
||||
"display": "none"
|
||||
});
|
||||
$('.dz-preview').remove();
|
||||
$('.markdown-area').trigger('input');
|
||||
|
||||
$uploadingProgressContainer.addClass('hide');
|
||||
$cancelButton.addClass('hide');
|
||||
}
|
||||
});
|
||||
child = $(dropzone[0]).children("textarea");
|
||||
|
||||
child = $(dropzone[0]).children('textarea');
|
||||
|
||||
// removeAllFiles(true) stops uploading files (if any)
|
||||
// and remove them from dropzone files queue.
|
||||
$cancelButton.on('click', (e) => {
|
||||
const target = e.target.closest('form').querySelector('.div-dropzone');
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
Dropzone.forElement(target).removeAllFiles(true);
|
||||
});
|
||||
|
||||
// If 'error' event is fired, we store a failed files,
|
||||
// clear dropzone files queue, change status of failed files to undefined,
|
||||
// and add that files to the dropzone files queue again.
|
||||
// addFile() adds file to dropzone files queue and upload it.
|
||||
$retryLink.on('click', (e) => {
|
||||
const dropzoneInstance = Dropzone.forElement(e.target.closest('form').querySelector('.div-dropzone'));
|
||||
const failedFiles = dropzoneInstance.files;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// 'true' parameter of removeAllFiles() cancels uploading of files that are being uploaded at the moment.
|
||||
dropzoneInstance.removeAllFiles(true);
|
||||
|
||||
failedFiles.map((failedFile, i) => {
|
||||
const file = failedFile;
|
||||
|
||||
if (file.status === Dropzone.ERROR) {
|
||||
file.status = undefined;
|
||||
file.accepted = undefined;
|
||||
}
|
||||
|
||||
return dropzoneInstance.addFile(file);
|
||||
});
|
||||
});
|
||||
|
||||
handlePaste = function(event) {
|
||||
var filename, image, pasteEvent, text;
|
||||
pasteEvent = event.originalEvent;
|
||||
|
@ -110,25 +160,27 @@ window.DropzoneInput = (function() {
|
|||
image = isImage(pasteEvent);
|
||||
if (image) {
|
||||
event.preventDefault();
|
||||
filename = getFilename(pasteEvent) || "image.png";
|
||||
text = "{{" + filename + "}}";
|
||||
filename = getFilename(pasteEvent) || 'image.png';
|
||||
text = `{{${filename}}}`;
|
||||
pasteText(text);
|
||||
return uploadFile(image.getAsFile(), filename);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
isImage = function(data) {
|
||||
var i, item;
|
||||
i = 0;
|
||||
while (i < data.clipboardData.items.length) {
|
||||
item = data.clipboardData.items[i];
|
||||
if (item.type.indexOf("image") !== -1) {
|
||||
if (item.type.indexOf('image') !== -1) {
|
||||
return item;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
pasteText = function(text, shouldPad) {
|
||||
var afterSelection, beforeSelection, caretEnd, caretStart, textEnd;
|
||||
var formattedText = text;
|
||||
|
@ -142,31 +194,33 @@ window.DropzoneInput = (function() {
|
|||
$(child).val(beforeSelection + formattedText + afterSelection);
|
||||
textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
return form_textarea.trigger("input");
|
||||
return formTextarea.trigger('input');
|
||||
};
|
||||
|
||||
getFilename = function(e) {
|
||||
var value;
|
||||
if (window.clipboardData && window.clipboardData.getData) {
|
||||
value = window.clipboardData.getData("Text");
|
||||
value = window.clipboardData.getData('Text');
|
||||
} else if (e.clipboardData && e.clipboardData.getData) {
|
||||
value = e.clipboardData.getData("text/plain");
|
||||
value = e.clipboardData.getData('text/plain');
|
||||
}
|
||||
value = value.split("\r");
|
||||
return value.first();
|
||||
};
|
||||
|
||||
uploadFile = function(item, filename) {
|
||||
var formData;
|
||||
formData = new FormData();
|
||||
formData.append("file", item, filename);
|
||||
formData.append('file', item, filename);
|
||||
return $.ajax({
|
||||
url: uploads_path,
|
||||
type: "POST",
|
||||
url: uploadsPath,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
dataType: "json",
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
headers: {
|
||||
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
beforeSend: function() {
|
||||
showSpinner();
|
||||
|
@ -183,44 +237,54 @@ window.DropzoneInput = (function() {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateAttachingMessage = (files, messageContainer) => {
|
||||
let attachingMessage;
|
||||
const filesCount = files.filter(function(file) {
|
||||
return file.status === 'uploading' ||
|
||||
file.status === 'queued';
|
||||
}).length;
|
||||
|
||||
// Dinamycally change uploading files text depending on files number in
|
||||
// dropzone files queue.
|
||||
if (filesCount > 1) {
|
||||
attachingMessage = 'Attaching ' + filesCount + ' files -';
|
||||
} else {
|
||||
attachingMessage = 'Attaching a file -';
|
||||
}
|
||||
|
||||
messageContainer.text(attachingMessage);
|
||||
};
|
||||
|
||||
insertToTextArea = function(filename, url) {
|
||||
return $(child).val(function(index, val) {
|
||||
return val.replace("{{" + filename + "}}", url);
|
||||
return val.replace(`{{${filename}}}`, url);
|
||||
});
|
||||
};
|
||||
|
||||
appendToTextArea = function(url) {
|
||||
return $(child).val(function(index, val) {
|
||||
return val + url + "\n";
|
||||
});
|
||||
};
|
||||
|
||||
showSpinner = function(e) {
|
||||
return form.find(".div-dropzone-spinner").css({
|
||||
"opacity": 0.7,
|
||||
"display": "inherit"
|
||||
});
|
||||
return $uploadingProgressContainer.removeClass('hide');
|
||||
};
|
||||
|
||||
closeSpinner = function() {
|
||||
return form.find(".div-dropzone-spinner").css({
|
||||
"opacity": 0,
|
||||
"display": "none"
|
||||
});
|
||||
return $uploadingProgressContainer.addClass('hide');
|
||||
};
|
||||
|
||||
showError = function(message) {
|
||||
var checkIfMsgExists, errorAlert;
|
||||
errorAlert = $(form).find('.error-alert');
|
||||
checkIfMsgExists = errorAlert.children().length;
|
||||
if (checkIfMsgExists === 0) {
|
||||
errorAlert.append(divAlert);
|
||||
return $(".div-dropzone-alert").append(btnAlert + message);
|
||||
}
|
||||
$uploadingErrorContainer.removeClass('hide');
|
||||
$uploadingErrorMessage.html(message);
|
||||
};
|
||||
closeAlertMessage = function() {
|
||||
return form.find(".div-dropzone-alert").alert("close");
|
||||
};
|
||||
form.find(".markdown-selector").click(function(e) {
|
||||
|
||||
form.find('.markdown-selector').click(function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.gfm-form').find('.div-dropzone').click();
|
||||
form_textarea.focus();
|
||||
formTextarea.focus();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -277,6 +277,7 @@
|
|||
.toolbar-text {
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
margin-top: 2px;
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
float: left;
|
||||
|
@ -402,3 +403,45 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.uploading-container {
|
||||
float: right;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.uploading-error-icon,
|
||||
.uploading-error-message {
|
||||
color: $gl-text-red;
|
||||
}
|
||||
|
||||
.uploading-error-message {
|
||||
@media (max-width: $screen-xs-max) {
|
||||
&::after {
|
||||
content: "\a";
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.uploading-progress {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.attach-new-file,
|
||||
.button-attach-file,
|
||||
.retry-uploading-link {
|
||||
color: $gl-link-color;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: 0;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.markdown-selector {
|
||||
color: $gl-link-color;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ module IconsHelper
|
|||
# font-awesome-rails gem, but should we ever use a different icon pack in the
|
||||
# future we won't have to change hundreds of method calls.
|
||||
def icon(names, options = {})
|
||||
if (options.keys & %w[aria-hidden aria-label]).empty?
|
||||
# Add `aria-hidden` if there are no aria's set
|
||||
if (options.keys & %w[aria-hidden aria-label data-hidden]).empty?
|
||||
# Add 'aria-hidden' and 'data-hidden' if they are not set in options.
|
||||
options['aria-hidden'] = true
|
||||
options['data-hidden'] = true
|
||||
end
|
||||
|
||||
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
|
||||
|
|
|
@ -9,6 +9,27 @@
|
|||
- else
|
||||
is
|
||||
supported
|
||||
%button.toolbar-button.markdown-selector{ type: 'button', tabindex: '-1' }
|
||||
= icon('file-image-o', class: 'toolbar-button-icon')
|
||||
Attach a file
|
||||
|
||||
%span.uploading-container
|
||||
%span.uploading-progress-container.hide
|
||||
= icon('file-image-o', class: 'toolbar-button-icon')
|
||||
%span.attaching-file-message
|
||||
-# Populated by app/assets/javascripts/dropzone_input.js
|
||||
%span.uploading-progress 0%
|
||||
%span.uploading-spinner
|
||||
= icon('spinner spin', class: 'toolbar-button-icon')
|
||||
|
||||
%span.uploading-error-container.hide
|
||||
%span.uploading-error-icon
|
||||
= icon('file-image-o', class: 'toolbar-button-icon')
|
||||
%span.uploading-error-message
|
||||
-# Populated by app/assets/javascripts/dropzone_input.js
|
||||
%button.retry-uploading-link{ type: 'button' } Try again
|
||||
or
|
||||
%button.attach-new-file.markdown-selector{ type: 'button' } attach a new file
|
||||
|
||||
%button.markdown-selector.button-attach-file{ type: 'button', tabindex: '-1' }
|
||||
= icon('file-image-o', class: 'toolbar-button-icon')
|
||||
Attach a file
|
||||
|
||||
%button.btn.btn-default.btn-xs.hide.button-cancel-uploading-files{ type: 'button' } Cancel
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add an ability to cancel attaching file and redesign attaching files UI
|
||||
merge_request: 9431
|
||||
author: blackst0ne
|
|
@ -5,18 +5,78 @@ feature 'User uploads file to note', feature: true do
|
|||
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:empty_project, creator: user, namespace: user.namespace) }
|
||||
let(:issue) { create(:issue, project: project, author: user) }
|
||||
|
||||
scenario 'they see the attached file', js: true do
|
||||
issue = create(:issue, project: project, author: user)
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
visit namespace_project_issue_path(project.namespace, project, issue)
|
||||
end
|
||||
|
||||
dropzone_file(Rails.root.join('spec', 'fixtures', 'dk.png'))
|
||||
click_button 'Comment'
|
||||
wait_for_ajax
|
||||
context 'before uploading' do
|
||||
it 'shows "Attach a file" button', js: true do
|
||||
expect(page).to have_button('Attach a file')
|
||||
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
expect(find('a.no-attachment-icon img[alt="dk"]')['src'])
|
||||
.to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
|
||||
context 'uploading is in progress' do
|
||||
it 'shows "Cancel" button on uploading', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
expect(page).to have_button('Cancel')
|
||||
end
|
||||
|
||||
it 'cancels uploading on clicking to "Cancel" button', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
click_button 'Cancel'
|
||||
|
||||
expect(page).to have_button('Attach a file')
|
||||
expect(page).not_to have_button('Cancel')
|
||||
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
|
||||
end
|
||||
|
||||
it 'shows "Attaching a file" message on uploading 1 file', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -')
|
||||
end
|
||||
|
||||
it 'shows "Attaching 2 files" message on uploading 2 file', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4'),
|
||||
Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching 2 files -')
|
||||
end
|
||||
|
||||
it 'shows error message, "retry" and "attach a new file" link a if file is too big', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4')], 0.01)
|
||||
|
||||
error_text = 'File is too big (0.06MiB). Max filesize: 0.01MiB.'
|
||||
|
||||
expect(page).to have_selector('.uploading-error-message', visible: true, text: error_text)
|
||||
expect(page).to have_selector('.retry-uploading-link', visible: true, text: 'Try again')
|
||||
expect(page).to have_selector('.attach-new-file', visible: true, text: 'attach a new file')
|
||||
expect(page).not_to have_button('Attach a file')
|
||||
end
|
||||
end
|
||||
|
||||
context 'uploading is complete' do
|
||||
it 'shows "Attach a file" button on uploading complete', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
|
||||
wait_for_ajax
|
||||
|
||||
expect(page).to have_button('Attach a file')
|
||||
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
|
||||
end
|
||||
|
||||
scenario 'they see the attached file', js: true do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
|
||||
click_button 'Comment'
|
||||
wait_for_ajax
|
||||
|
||||
expect(find('a.no-attachment-icon img[alt="dk"]')['src'])
|
||||
.to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,32 +6,52 @@ module DropzoneHelper
|
|||
# Dropzone events to perform the actual upload.
|
||||
#
|
||||
# This method waits for the upload to complete before returning.
|
||||
def dropzone_file(file_path)
|
||||
# max_file_size is an optional parameter.
|
||||
# If it's not 0, then it used in dropzone.maxFilesize parameter.
|
||||
# wait_for_queuecomplete is an optional parameter.
|
||||
# If it's 'false', then the helper will NOT wait for backend response
|
||||
# It lets to test behaviors while AJAX is processing.
|
||||
def dropzone_file(files, max_file_size = 0, wait_for_queuecomplete = true)
|
||||
# Generate a fake file input that Capybara can attach to
|
||||
page.execute_script <<-JS.strip_heredoc
|
||||
$('#fakeFileInput').remove();
|
||||
var fakeFileInput = window.$('<input/>').attr(
|
||||
{id: 'fakeFileInput', type: 'file'}
|
||||
{id: 'fakeFileInput', type: 'file', multiple: true}
|
||||
).appendTo('body');
|
||||
|
||||
window._dropzoneComplete = false;
|
||||
JS
|
||||
|
||||
# Attach the file to the fake input selector with Capybara
|
||||
attach_file('fakeFileInput', file_path)
|
||||
# Attach files to the fake input selector with Capybara
|
||||
attach_file('fakeFileInput', files)
|
||||
|
||||
# Manually trigger a Dropzone "drop" event with the fake input's file list
|
||||
page.execute_script <<-JS.strip_heredoc
|
||||
var fileList = [$('#fakeFileInput')[0].files[0]];
|
||||
var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
|
||||
|
||||
var dropzone = $('.div-dropzone')[0].dropzone;
|
||||
dropzone.options.autoProcessQueue = false;
|
||||
|
||||
if (#{max_file_size} > 0) {
|
||||
dropzone.options.maxFilesize = #{max_file_size};
|
||||
}
|
||||
|
||||
dropzone.on('queuecomplete', function() {
|
||||
window._dropzoneComplete = true;
|
||||
});
|
||||
dropzone.listeners[0].events.drop(e);
|
||||
|
||||
var fileList = [$('#fakeFileInput')[0].files];
|
||||
|
||||
$.map(fileList, function(file){
|
||||
var e = jQuery.Event('drop', { dataTransfer : { files : file } });
|
||||
|
||||
dropzone.listeners[0].events.drop(e);
|
||||
});
|
||||
|
||||
dropzone.processQueue();
|
||||
JS
|
||||
|
||||
# Wait until Dropzone's fired `queuecomplete`
|
||||
loop until page.evaluate_script('window._dropzoneComplete === true')
|
||||
if wait_for_queuecomplete
|
||||
# Wait until Dropzone's fired `queuecomplete`
|
||||
loop until page.evaluate_script('window._dropzoneComplete === true')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue