Skip to content

Commit

Permalink
exposes test helper form-data and follow FormData specification
Browse files Browse the repository at this point in the history
  • Loading branch information
jelhan committed Jun 28, 2019
1 parent dd3f31a commit 8e43e7d
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 43 deletions.
106 changes: 106 additions & 0 deletions test-support/helpers/form-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Allows to inject and remove a FormData polyfill which supports get() and
* getAll() methods.
*
* Except for Chrome (>= 50) and Firefox (>= 39) major browser engines implement
* only a very basic subset of FormData specification. Especially current Safari,
* IE, Edge and PhantomJS does not support any methods to retrieve data of a
* FormData object.
* This is a hard limitation in testing. E.g. in an ember-cli-mirage route handler
* you are not able to retrieve values of the request.
*
* Implementation follows FormData specification:
* https://xhr.spec.whatwg.org/#interface-formdata
*/

import { isArray } from '@ember/array';

function TestableFormData() {
this._data = {};
}

/*
* Injects FormData polyfill by overriding window.FormData if current browser
* engine does not implement FormData.get method.
* Overriding window.FormData could be forced by passing `true` as first argument.
*/
TestableFormData.inject = function(force) {
if (
window &&
(force || typeof window.FormData.get === 'undefined')
) {
this.OldFormData = window.FormData;
window.FormData = TestableFormData;
}
};

TestableFormData.remove = function() {
if (window && this.OldFormData) {
window.FormData = this.OldFormData;
delete this.OldFormData;
}
};

/*
* FormData.append()
* The append(name, value) and append(name, blobValue, filename) methods, when
* invoked, must run these steps:
* 1. Let value be value if given, and blobValue otherwise.
* 2. Let entry be the result of creating an entry with name, value, and
* filename if given.
* 3. Append entry to context object’s list of entries.
* Note: The reason there is an argument named value as well as blobValue is
* due to a limitation of the editing software used to write the XMLHttpRequest
* Standard.
* https://xhr.spec.whatwg.org/#dom-formdata-append
*/
TestableFormData.prototype.append = function(name, value, filename) {
if (!isArray(this._data[name])) {
this._data[name] = [];
}
/*
* To create an entry for name, value, and optionally a filename, run these steps:
* 3. If value is a Blob object and not a File object, then set value to a
* new File object, representing the same bytes, whose name attribute
* value is "blob".
* 4. If value is (now) a File object and filename is given, then set value
* to a new File object, representing the same bytes, whose name attribute
* value is filename.
* https://xhr.spec.whatwg.org/#create-an-entry
*/
if (
// it's a Blob
value instanceof Blob &&
// but it's not a File yet
!(value instanceof File) &&
// File is supported by current engine
typeof File === 'function'
) {
value = new File([value], filename || 'blob');
}
this._data[name].push(value);
};

/*
* FormData.get()
* The get(name) method, when invoked, must return the value of the first entry
* whose name is name, and null otherwise.
* https://xhr.spec.whatwg.org/#dom-formdata-get
*/
TestableFormData.prototype.get = function(name) {
let values = this._data[name];
return ( isArray(values) && values.length > 0 ) ? values[0] : null;
};

/*
* FormData.getAll()
* The getAll(name) method, when invoked, must return the values of all entries
* whose name is name, in list order, and the empty sequence otherwise.
* https://xhr.spec.whatwg.org/#dom-formdata-getall
*/
TestableFormData.prototype.getAll = function(name) {
let value = this._data[name];
return isArray(value) ? value : [];
};

export default TestableFormData;
38 changes: 0 additions & 38 deletions tests/helpers/form-data.js

This file was deleted.

65 changes: 65 additions & 0 deletions tests/unit/test-helpers/form-data-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import TestableFormData from 'dummy/tests/helpers/form-data';
import { isArray } from '@ember/array';
import { warn } from '@ember/debug';

module("test helper | FormData", function(hooks) {
setupTest(hooks);

test('supports empty value', function(assert) {
let formData = new TestableFormData();

assert.strictEqual(formData.get('not-existing'), null, 'get returns null');
assert.ok(isArray(formData.getAll('not-existing')), 'getAll returns an array');
assert.equal(formData.getAll('not-existing').length, 0, 'array returned by getAll is empty');
});

test('supports single value', function(assert) {
let formData = new TestableFormData();
formData.append('foo', 'a');

assert.strictEqual(formData.get('foo'), 'a', 'get returns value');
assert.ok(isArray(formData.getAll('foo')), 'getAll returns an array');
assert.equal(formData.getAll('foo')[0], 'a', 'array returned by getAll contains value');
});

test('supports multiple values', function(assert) {
let formData = new TestableFormData();
formData.append('foo', 'a');
formData.append('foo', 'b');

assert.strictEqual(formData.get('foo'), 'a', 'get returns value which was set as first');
assert.ok(isArray(formData.getAll('foo')), 'getAll returns an array');
assert.equal(formData.getAll('foo')[0], 'a', 'array returned by getAll contains first value');
assert.equal(formData.getAll('foo')[1], 'b', 'array returned by getAll contains second value');
})

test('supports appending Blob', function(assert) {
let formData = new TestableFormData();
formData.append('foo', new Blob([]));

assert.ok(isArray(formData.getAll('foo')), 'getAll returns an array');
assert.ok(formData.getAll('foo')[0] instanceof Blob, 'array returned by getAll contains value');
});

test('supports appending File', function(assert) {
if (typeof File !== 'function') {
warn('Skipping File tests since File not supported by current engine');
assert.expect(0);
return;
}

let formData = new TestableFormData();
let fileObject = new File([], 'untouched');
formData.append('foo', new Blob([]));
formData.append('foo', fileObject);
formData.append('foo', new Blob([]), 'test.jpg');

assert.ok(formData.getAll('foo')[0] instanceof File, 'converts Blob to File object');
assert.equal(formData.getAll('foo')[0].name, 'blob', 'sets name to blob if no name is given');
assert.strictEqual(formData.getAll('foo')[1], fileObject, 'File object does not get changed');
assert.ok(formData.getAll('foo')[2] instanceof File, 'converts Blob to File object');
assert.equal(formData.getAll('foo')[2].name, 'test.jpg', 'supports specifing name');
});
});
13 changes: 8 additions & 5 deletions tests/unit/uploader-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { computed } from '@ember/object';
import $ from 'jquery';
import Uploader from 'ember-uploader/uploaders/uploader';
import test from 'ember-sinon-qunit/test-support/test';
import TestableFormData from '../helpers/form-data';
import TestableFormData from 'dummy/tests/helpers/form-data';
import { startMirage } from 'dummy/initializers/ember-cli-mirage';
import { isArray } from '@ember/array';

let file;

Expand Down Expand Up @@ -77,11 +78,13 @@ module('EmberUploader.Uploader', function(hooks) {
let uploader = Uploader.extend({
paramName: 'files'
}).create();
let formData = uploader.createFormData([1, 2, 3]);

let formData = uploader.createFormData([1,2,3]);
assert.equal(formData.data['files'][0], 1);
assert.equal(formData.data['files'][1], 2);
assert.equal(formData.data['files'][2], 3);
assert.strictEqual(formData.get('files'), null, 'does not set to name without empty brackets');

let files = formData.getAll('files[]');
assert.ok(isArray(files));
assert.deepEqual(files, [1, 2, 3])
});

test("uploads to the given url", function(assert) {
Expand Down

0 comments on commit 8e43e7d

Please sign in to comment.