Skip to content

Commit a8b3946

Browse files
committed
[WIP] Adding tests for the code
1 parent 542ac96 commit a8b3946

File tree

7 files changed

+375
-57
lines changed

7 files changed

+375
-57
lines changed

.babelrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"presets": [
3+
"es2015"
4+
]
5+
}

.gitignore

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,4 @@
1-
# Logs
2-
logs
3-
*.log
4-
npm-debug.log*
5-
yarn-debug.log*
6-
yarn-error.log*
7-
8-
# Runtime data
9-
pids
10-
*.pid
11-
*.seed
12-
*.pid.lock
13-
14-
# Directory for instrumented libs generated by jscoverage/JSCover
15-
lib-cov
16-
17-
# Coverage directory used by tools like istanbul
18-
coverage
19-
20-
# nyc test coverage
21-
.nyc_output
22-
23-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24-
.grunt
25-
26-
# Bower dependency directory (https://bower.io/)
27-
bower_components
28-
29-
# node-waf configuration
30-
.lock-wscript
31-
32-
# Compiled binary addons (http://nodejs.org/api/addons.html)
33-
build/Release
34-
35-
# Dependency directories
361
node_modules/
37-
jspm_packages/
38-
39-
# Typescript v1 declaration files
40-
typings/
41-
42-
# Optional npm cache directory
432
.npm
44-
45-
# Optional eslint cache
46-
.eslintcache
47-
48-
# Optional REPL history
49-
.node_repl_history
50-
51-
# Output of 'npm pack'
52-
*.tgz
53-
54-
# Yarn Integrity file
55-
.yarn-integrity
56-
57-
# dotenv environment variables file
58-
.env
59-
3+
yarn.lock
4+
dist/

package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "form-js",
3+
"version": "0.1.0",
4+
"description": "A javascript representation of a form",
5+
"main": "dist/Form.js",
6+
"scripts": {
7+
"test": "node ./node_modules/jest/bin/jest",
8+
"build": "node ./node_modules/babel-cli/bin/babel src --out-dir dist"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/santigarcor/form-js.git"
13+
},
14+
"keywords": [
15+
"form"
16+
],
17+
"author": "Santiago Garcia (http://santigarcor.me)",
18+
"license": "MIT",
19+
"bugs": {
20+
"url": "https://github.com/santigarcor/form-js/issues"
21+
},
22+
"homepage": "https://github.com/santigarcor/form-js#readme",
23+
"devDependencies": {
24+
"babel-cli": "^6.26.0",
25+
"babel-jest": "^21.2.0",
26+
"babel-preset-es2015": "^6.24.1",
27+
"jest": "^21.2.1",
28+
"moxios": "^0.4.0"
29+
},
30+
"dependencies": {
31+
"axios": "^0.16.2"
32+
}
33+
}

src/Form.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
let FormErrors = require('./FormErrors');
2+
let RequestDataContainer = require('./RequestDataContainer');
3+
4+
if (window.axios == 'undefined') {
5+
let axios = require('axios');
6+
}
7+
8+
/**
9+
* this class handles everything that concerns
10+
* about forms. Submit and errors and considered too
11+
*/
12+
class Form {
13+
14+
/**
15+
* Set up the form class setting the data properties
16+
* directly to the form properties
17+
*
18+
* @param {Object} data
19+
* @param {Boolean} clearAfterResponse
20+
* @return {void}
21+
*/
22+
constructor(data, method = 'post', clearAfterResponse = false) {
23+
this.originalData = data;
24+
Object.assign(this, data);
25+
this.method = method;
26+
this.clearAfterResponse = clearAfterResponse;
27+
this.errors = new FormErrors();
28+
}
29+
30+
static makeFrom(data, method = 'post', clearAfterResponse = false) {
31+
return new this(data, method, clearAfterResponse);
32+
}
33+
34+
/**
35+
* Reset the form data
36+
*
37+
* @return {void}
38+
*/
39+
reset() {
40+
Object.assign(this, this.originalData);
41+
this.errors.clear();
42+
}
43+
44+
/**
45+
* Return the form data
46+
*
47+
* @todo Add the option to choose between form data and plain object
48+
* @return {Object}
49+
*/
50+
data(type) {
51+
let container = new RequestDataContainer(type);
52+
53+
for (let field in this.originalData) {
54+
container.set(field, this[field]);
55+
}
56+
57+
if (this.method != 'post') {
58+
container.set('_method', this.method);
59+
}
60+
61+
return container.data;
62+
}
63+
64+
/**
65+
* Set the form method.
66+
*
67+
* @param {String} method
68+
*/
69+
setMethod(method) {
70+
this.method = method;
71+
72+
return this;
73+
}
74+
75+
/**
76+
* Get the errors for a field.
77+
*
78+
* @param {String} field
79+
* @return {String}
80+
*/
81+
errorsFor(field) {
82+
return this.errors.get(field);
83+
}
84+
85+
/**
86+
* Submit the form and return a promise
87+
*
88+
* @param {string} url
89+
* @return {Promise}
90+
*/
91+
submit(url, dataType = 'form_data') {
92+
return new Promise((resolve, reject) => {
93+
axios.post(url, this.data(dataType))
94+
.then(response => {
95+
this.formSubmitSucceded(response);
96+
resolve(response);
97+
})
98+
.catch(error => {
99+
this.formSubmitFailed(error);
100+
reject(error);
101+
});
102+
});
103+
}
104+
105+
/**
106+
* Perform actions after submition succeded
107+
*
108+
* @param {Object} response
109+
* @return {void}
110+
*/
111+
formSubmitSucceded(response) {
112+
if (this.clearAfterResponse) {
113+
this.reset();
114+
}
115+
}
116+
117+
/**
118+
* Perform actions after submition failed
119+
*
120+
* @param {Object} errors
121+
* @return {void}
122+
*/
123+
formSubmitFailed(errors) {
124+
if (errors.response.status == 422) {
125+
this.errors.record(errors.response.data.errors);
126+
}
127+
}
128+
}
129+
130+
module.exports = Form;

src/FormErrors.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* this class handles the possible errors
3+
* in a form
4+
*/
5+
export default class FormErrors {
6+
7+
/**
8+
* Create an instance of formerrors
9+
* @return {void}
10+
*/
11+
constructor() {
12+
this.errors = {};
13+
}
14+
15+
/**
16+
* Check if the field exists in the errors
17+
*
18+
* @param {String} field
19+
* @return {Boolean}
20+
*/
21+
has(field) {
22+
return this.errors.hasOwnProperty(field);
23+
}
24+
25+
/**
26+
* Return the error for the field
27+
*
28+
* @param {String} field
29+
* @return {String}
30+
*/
31+
get(field) {
32+
if (!this.has(field)) {
33+
return null;
34+
}
35+
36+
return this.errors[field][0];
37+
}
38+
39+
/**
40+
* Record new errors
41+
*
42+
* @param {Object} errors
43+
* @return {void}
44+
*/
45+
record(errors) {
46+
this.errors = errors;
47+
}
48+
49+
/**
50+
* Clear error for specific field or for all errors
51+
*
52+
* @param {String} field
53+
* @return {void}
54+
*/
55+
clear(field) {
56+
if (!field) {
57+
this.errors = {};
58+
return;
59+
}
60+
61+
delete this.errors[field];
62+
}
63+
64+
/**
65+
* Check if there are any errors
66+
*
67+
* @return {Boolean}
68+
*/
69+
any() {
70+
return Object.keys(this.errors).length > 0;
71+
}
72+
}
73+
74+
module.exports = FormErrors;

src/RequestDataContainer.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
class RequestDataContainer {
2+
3+
/**
4+
* Set up the class.
5+
*
6+
* @param {String} type
7+
* @return {this}
8+
*/
9+
constructor(type) {
10+
this.type = type;
11+
this.data = type == 'json' ? {} : (new FormData());
12+
}
13+
14+
/**
15+
* Set a value in the respective key inside the data object.
16+
*
17+
* @param {String} key
18+
* @param {mixed} value
19+
*/
20+
set(key, value) {
21+
if (this.type == 'json') {
22+
this.data[key] = value;
23+
} else if (this.type == 'form-data') {
24+
if (Array.isArray(value)) {
25+
value.forEach(element => {
26+
this.data.append(`${key}[]`, this.parseToFormData(element));
27+
});
28+
} else {
29+
this.data.append(key, this.parseToFormData(value));
30+
}
31+
}
32+
}
33+
34+
/**
35+
* Parse the value to the FormData value type.
36+
*
37+
* @param {mixed} value
38+
* @return {string}
39+
*/
40+
parseToFormData(value) {
41+
if (typeof(value) === "boolean") {
42+
return value ? 1 : 0;
43+
}
44+
45+
return value == null ? '' : value;
46+
}
47+
}
48+
49+
module.exports = RequestDataContainer;

0 commit comments

Comments
 (0)