-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue Acceptance Testing Uploads #123
Comments
I guess I'm not totally sure what you are trying to do when you say that you cannot change the files of an input type. I understand that you cannot dynamically set the files of an input but you can simulate it by bypassing the input or messing with the change event which I think is the route you have taken. I have some tests around the uploader and input that use a |
Yep, this is fine in Unit tests like what you have. But in Acceptance Tests, you need to fire the event on some kind of DOM element. Since the component relies on the change event on the input element, this is where I get stuck. Once I wrap up the tests in our app, I'll create a gist or twiddle to demonstrate what I mean in more detail. |
@bryanaka Faced a similar issue, how about doing the following: // tests/helpers/start-app.js
export default function startApp(attrs) {
...
let attributes = Ember.merge({
customEvents: {
filesDidChange: 'filesDidChange'
}
}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
...
}
// tests/acceptance/home-test.js
test("something that needs to work", function(assert) {
...
triggerEvent('.Home-files__file-input', 'filesDidChange');
...
}); |
Also interested in a way to do acceptance test upload simulation. Have tried to use |
I'm working on a small example project to help with this. |
I am also running into this issue right now. Would be helpful to get some guidance. Thanks for the cool library! |
ember-cli-file-picker provides test helpers. You could mock a change event. Files could be mocked by Blob object and used as event target. But a mocked event having a custom target does not fire embers change event. It's only working in ember-cli-file-picker cause it registers jQuery event handler in didInsertElement hook. I've asked at stackoverflow about the strange difference between jQuery event handler and ember event. Perhaps someone has an idea. Otherwise we could use same approach as ember-cli-file-picker. |
@marpo60 , your example helps in triggering the "filesDidChange" event and we get a little further along in testing but it doesn't solve the remaining hurdle for testing file upload which relates to the actual "files" object. Triggering that event does me no good if my "files" object is empty and I have no way to modify it with dummy file data. I'm trying to test that filenames are valid (e.g. don't contain special characters, whitespace, etc.) My "filesDidChange" method handles it but since I can't modify the files object from my test I can't test that the UI reflects the violation to the user. |
Best solution to date given that as of Ember 2.5, "event.target" is now read only (emberjs/ember.js#13540) appears to be the following post from @chrism: https://medium.com/@chrisdmasters/acceptance-testing-file-uploads-in-ember-2-5-1c9c8dbe5368 Chris's approach with a few modifications to the component have met my need. My approach is as follows for those who are still looking for a solution: file-upload.js component
|
after some debugging, i found a solution where no adjustments on the original code have to be made. The triggerEvent function used in ember acceptance tests checks for the change-event and creates a special FileEvent: Only thing to do is, pass created Blob-Files in an array as options: /* global Blob */
import { module, test } from 'qunit';
import { triggerEvent } from '@ember/test-helpers';
module('Acceptance | my tests', function(hooks) {
setupApplicationTest(hooks);
test('my file upload works', async function(assert) {
// ...
const imageFile = new Blob(['test'], {type : 'image/png'});
imageFile.name = 'my-image.png';
await triggerEvent('.my-wrapper input[type=file]', 'change', [imageFile] );
// ... assert something
}
} |
Here, is my solution for this, which doesn't require any changes to the source code. All of the modifications will be performed only in the Also, all comments and description of what/how/why are in the code.
export default function startApp(attrs) {
let attributes = merge({
// overriding default file upload event name to make `ember-uploader` work in test env
customEvents: {
filesDidChange: 'filesDidChange'
}
}, config.APP);
attributes = merge(attributes, attrs); // use defaults, but you can override;
// NOTE: Here goes rest of the code
}
import Ember from 'ember';
// In our app we also have limits on the size of uploaded files
// If yours doesn't - than you should get rid of `fileContentTypesBySize`, `fileContentProxy` and other
import {
FILE_SIZE_MIN_LIMIT as fileMinLimit,
FILE_SIZE_MAX_LIMIT as fileMaxLimit,
} from 'voicereel/utils/constants';
const {
Test: { registerAsyncHelper }
} = Ember;
const { floor, ceil } = Math;
const fileContentTypesBySize = {
min: floor(fileMinLimit),
normal: ceil((fileMinLimit + fileMaxLimit) / 2),
max: ceil(fileMaxLimit),
};
// Generates fake body for the file
const fileContentProxy = new Proxy(fileContentTypesBySize, {
get(target, property) {
const content = Array(target[property] || target['normal']).fill(' ').join('');
return [content];
}
});
// Default values for the `uploadFile` options
const defaultOptions = {
contentType: 'normal',
type: 'text/html',
name: 'index.html',
amount: 1,
};
/**
* @description Creating fake file which we will 'upload'
*
* @param {Object} options - Options passed to configure generated file
* @param {String} [options.contentType='normal'] - Size of the file to be uploaded
* @param {String} [options.name='index.html'] - Name of the file to be uploaded
* @param {String} [options.type='text/html'] - Type of the file to be uploaded
*
* @return {Object} Created file
*/
function createFile({ contentType, name, type }) {
const content = fileContentProxy[contentType];
const file = new Blob(content, { type }); // NOTE: `Blob` constructor takes array of content as first argument
file.name = name;
return file;
}
/**
* @description Upload file testing helper
*
* @param {Object} app - Application instance passed by ember directly
* @param {String} selector - CSS selector where file should be uploaded
* @param {Object} options - File configuration and uploading options
* @param {String} [options.contentType='normal'] - Size of the file to be uploaded(gives ability to test uploaded file size validations)
* @param {Number} [options.amount=1] - Amount of the files to be uploaded(gives ability to test uploaded files amount validations)
* @param {String} [options.name='index.html'] - Name of the file to be uploaded
* @param {String} [options.type='text/html'] - Type of the file to be uploaded
*
* @return {Promise} Triggered event promise
*/
export default registerAsyncHelper('uploadFile', function (app, selector, {
contentType,
amount,
name,
type,
} = defaultOptions) {
const file = createFile({ contentType, name, type }); // Generating file
// As in our app we have a limit on the files, that can be uploaded at once
// Here we are configuring the amount of the files to be uploaded
const files = Array(amount).fill(file);
return triggerEvent(
selector,
'filesDidChange',
{ files }
);
}); and finally our test file will look like
import { test } from 'qunit';
import page from 'APP_NAME/tests/pages/upload-demos';
import HTMLFileUpload from 'APP_NAME/components/html-file-upload';
import moduleForAcceptance from 'voicereel/tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | upload html file', {
beforeEach() {
// Here we are overriding our custom upload component.
// To make sure that when `filesDidChange` is called
// An array of files is being passed as an argument, but not the event, that was fired
this.application.__container__.registry.register('component:html-file-upload', HTMLFileUpload.extend({
filesDidChange(uploadedFiles) {
// In test environment, `uploadedFiles` argument is an event that was fired
// Here we are trying to get the files, that were uploaded
const { files } = uploadedFiles.originalEvent;
return this._super(files);
}
}));
}
});
test('Upload single .html file', function(assert) {
assert.expect(1);
page
.visit()
.then(() => uploadFile('.html-uploader'))
.then(() => {
assert.equal(page.uploadedFiles.length, 1, 'One file was uploaded');
});
});
test('Upload multiple .html files', function(assert) {
assert.expect(1);
page
.visit()
.then(() => uploadFile('.html-uploader', {
contentType: 'normal',
type: 'text/html',
name: 'index.html',
amount: 5,
}))
.then(() => {
assert.equal(page.uploadedFiles.length, 5, 'All 5 files were succecfully uploaded');
});
}); This is not a final solution, in some places it can be improved(for example when you want to configure any option of the P.S. @bryanaka @digitaltoad What do you think about this solution? |
Just got here again.. You all rock! |
I am trying to write acceptance tests against uploading files using this lib. I've gotten to the point where I can correctly simulate all of the users' actions, including generating a mock file.
The problem is here:
https://github.com/benefitcloud/ember-uploader/blob/master/addon/components/file-field.js#L10
For security reasons, you are not allowed to change the files of an input type:
http://stackoverflow.com/questions/1017224/dynamically-set-value-of-a-file-input
So it makes it near impossible (unless I am missing something) to write an automated test simulating a user uploading a file.
I can understand this being a sensible route to go in production, but it would be nice to have a build in solution in test/dev that allows programmatic manipulation of the files.
right now in our app we are overwriting to do this
Any suggestions on how to do this better?
The text was updated successfully, but these errors were encountered: