factory_bot_instrumentation 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +30 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +43 -0
- data/.simplecov +3 -0
- data/.travis.yml +28 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +161 -0
- data/LICENSE +21 -0
- data/Makefile +96 -0
- data/README.md +713 -0
- data/Rakefile +8 -0
- data/app/assets/config/factory_bot_instrumentation_manifest.js +2 -0
- data/app/assets/javascripts/factory_bot_instrumentation/application.js +16 -0
- data/app/assets/javascripts/factory_bot_instrumentation/create.js +134 -0
- data/app/assets/javascripts/factory_bot_instrumentation/hooks.js +123 -0
- data/app/assets/javascripts/factory_bot_instrumentation/lib/form.js +66 -0
- data/app/assets/javascripts/factory_bot_instrumentation/lib/utils.js +116 -0
- data/app/assets/stylesheets/factory_bot_instrumentation/application.css +91 -0
- data/app/controllers/factory_bot/instrumentation/application_controller.rb +7 -0
- data/app/controllers/factory_bot/instrumentation/root_controller.rb +113 -0
- data/app/views/factory_bot/instrumentation/application/_config.html.erb +5 -0
- data/app/views/factory_bot/instrumentation/application/_scripts.html.erb +18 -0
- data/app/views/factory_bot/instrumentation/application/_spinner.html.erb +7 -0
- data/app/views/factory_bot/instrumentation/application/_styles.html.erb +20 -0
- data/app/views/factory_bot/instrumentation/root/index.html.erb +31 -0
- data/app/views/factory_bot_instrumentation/_blocks.html.erb +6 -0
- data/app/views/factory_bot_instrumentation/_navigation.html.erb +13 -0
- data/app/views/factory_bot_instrumentation/_scripts.html.erb +5 -0
- data/app/views/factory_bot_instrumentation/_styles.html.erb +5 -0
- data/app/views/layouts/factory_bot/instrumentation/application.html.erb +29 -0
- data/bin/rails +14 -0
- data/config/instrumentation.yml +55 -0
- data/config/routes.rb +8 -0
- data/doc/assets/blocks.png +0 -0
- data/doc/assets/customized.png +0 -0
- data/doc/assets/navigation.png +0 -0
- data/doc/assets/project.svg +68 -0
- data/doc/assets/regular.png +0 -0
- data/docker-compose.yml +8 -0
- data/factory_bot_instrumentation.gemspec +33 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4.gemfile.lock +147 -0
- data/gemfiles/rails_5.0.gemfile +7 -0
- data/gemfiles/rails_5.0.gemfile.lock +154 -0
- data/gemfiles/rails_5.1.gemfile +7 -0
- data/gemfiles/rails_5.1.gemfile.lock +154 -0
- data/gemfiles/rails_5.2.gemfile +7 -0
- data/gemfiles/rails_5.2.gemfile.lock +162 -0
- data/lib/factory_bot/instrumentation/configuration.rb +20 -0
- data/lib/factory_bot/instrumentation/engine.rb +19 -0
- data/lib/factory_bot/instrumentation/version.rb +7 -0
- data/lib/factory_bot_instrumentation.rb +43 -0
- metadata +209 -0
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will
|
2
|
+
// include all the files listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts,
|
5
|
+
// vendor/assets/javascripts, or any plugin's vendor/assets/javascripts
|
6
|
+
// directory can be referenced here using a relative path.
|
7
|
+
//
|
8
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at
|
9
|
+
// the bottom of the compiled file. JavaScript code in this file should be
|
10
|
+
// added after the last require_* statement.
|
11
|
+
//
|
12
|
+
// Read Sprockets README
|
13
|
+
// (https://github.com/rails/sprockets#sprockets-directives) for details about
|
14
|
+
// supported directives.
|
15
|
+
//
|
16
|
+
//= require_tree .
|
@@ -0,0 +1,134 @@
|
|
1
|
+
window.CreateForm = CreateForm = function()
|
2
|
+
{
|
3
|
+
this.scope = '#generate';
|
4
|
+
this.form = new Form(this.scope);
|
5
|
+
this.scenarios = window.scenarios;
|
6
|
+
this.select = $(`${this.scope} .scenario`);
|
7
|
+
this.desc = $(`${this.scope} .description`);
|
8
|
+
|
9
|
+
let self = this;
|
10
|
+
|
11
|
+
this.form.errorContent = function(payload, output, cb)
|
12
|
+
{
|
13
|
+
window.utils.waterfallWithHooks({
|
14
|
+
data: {
|
15
|
+
alert: `An unexpected error occured. Looks like something went wrong
|
16
|
+
while generating your new entity. This migth be a bug, or an
|
17
|
+
unexpected feature. It could be a temporary issue. When is is
|
18
|
+
persistent contact your friendly API Instrumentation
|
19
|
+
administrator.`,
|
20
|
+
output: output,
|
21
|
+
payload: payload,
|
22
|
+
pre: '',
|
23
|
+
post: ''
|
24
|
+
},
|
25
|
+
pre: window.hooks.preCreateError,
|
26
|
+
post: window.hooks.postCreateError,
|
27
|
+
action: (payload, innerCb) => {
|
28
|
+
cb(null, `
|
29
|
+
${payload.pre}
|
30
|
+
<div class="alert alert-danger" role="alert">${payload.alert}</div>
|
31
|
+
<pre id="data">${payload.output}</pre>
|
32
|
+
${window.utils.clipboardButton()}
|
33
|
+
${payload.post}
|
34
|
+
`);
|
35
|
+
innerCb(null, payload);
|
36
|
+
}
|
37
|
+
});
|
38
|
+
};
|
39
|
+
|
40
|
+
this.form.resultContent = function(payload, output, cb)
|
41
|
+
{
|
42
|
+
let card = window.utils.card({
|
43
|
+
body: `
|
44
|
+
<pre id="data">${output}</pre>
|
45
|
+
${window.utils.clipboardButton()}
|
46
|
+
`
|
47
|
+
});
|
48
|
+
|
49
|
+
window.utils.waterfallWithHooks({
|
50
|
+
data: {
|
51
|
+
alert: `A new ${self.scenario().name.toLowerCase()} was created.`,
|
52
|
+
output: output,
|
53
|
+
payload: payload,
|
54
|
+
cards: [card],
|
55
|
+
pre: '',
|
56
|
+
post: '',
|
57
|
+
openCard: '#details'
|
58
|
+
},
|
59
|
+
pre: window.hooks.preCreateResult,
|
60
|
+
post: window.hooks.postCreateResult,
|
61
|
+
action: (payload, innerCb) => {
|
62
|
+
cb(null, `
|
63
|
+
${payload.pre}
|
64
|
+
<div class="alert alert-success" role="alert">${payload.alert}</div>
|
65
|
+
<div class="accordion" id="response">
|
66
|
+
${payload.cards.join(' ')}
|
67
|
+
</div>
|
68
|
+
${payload.post}
|
69
|
+
`);
|
70
|
+
innerCb(null, payload);
|
71
|
+
if (payload.openCard) {
|
72
|
+
$(
|
73
|
+
`.accordion#response button[data-target="${payload.openCard}"]`
|
74
|
+
).click();
|
75
|
+
}
|
76
|
+
}
|
77
|
+
});
|
78
|
+
};
|
79
|
+
};
|
80
|
+
|
81
|
+
CreateForm.prototype.updateDesc = function()
|
82
|
+
{
|
83
|
+
this.desc.html(this.scenario().desc);
|
84
|
+
};
|
85
|
+
|
86
|
+
CreateForm.prototype.activeScenario = function()
|
87
|
+
{
|
88
|
+
raw = this.select.find(':selected').val().split('/');
|
89
|
+
return { group: raw[0], index: raw[1] };
|
90
|
+
};
|
91
|
+
|
92
|
+
CreateForm.prototype.scenario = function()
|
93
|
+
{
|
94
|
+
scenario = this.activeScenario();
|
95
|
+
return this.scenarios[scenario.group][scenario.index];
|
96
|
+
};
|
97
|
+
|
98
|
+
CreateForm.prototype.bind = function()
|
99
|
+
{
|
100
|
+
this.form.bind((event) => {
|
101
|
+
this.submit();
|
102
|
+
});
|
103
|
+
|
104
|
+
this.select.on('change', this.updateDesc);
|
105
|
+
this.updateDesc();
|
106
|
+
};
|
107
|
+
|
108
|
+
CreateForm.prototype.submit = function()
|
109
|
+
{
|
110
|
+
let form = this.form;
|
111
|
+
let conf = this.scenario();
|
112
|
+
|
113
|
+
window.utils.waterfallWithHooks({
|
114
|
+
data: {
|
115
|
+
factory: conf.factory,
|
116
|
+
traits: conf.traits,
|
117
|
+
overwrite: conf.overwrite
|
118
|
+
},
|
119
|
+
pre: window.hooks.preCreate,
|
120
|
+
post: window.hooks.postCreate,
|
121
|
+
action: (payload, cb) => {
|
122
|
+
window.utils.request({
|
123
|
+
url: window.createUrl,
|
124
|
+
data: JSON.stringify(payload)
|
125
|
+
}, (err, result) => {
|
126
|
+
if (err) { return cb && cb(err); }
|
127
|
+
cb(null, { request: payload, response: result });
|
128
|
+
});
|
129
|
+
}
|
130
|
+
}, function(err, result) {
|
131
|
+
if (err) { return form.showError(err, err.responseText); }
|
132
|
+
form.showResult(result, result.response);
|
133
|
+
});
|
134
|
+
};
|
@@ -0,0 +1,123 @@
|
|
1
|
+
// You can define some custom hooks to enhance the functionality. With the help
|
2
|
+
// of the following hooks you are able to customize the outputs, perform
|
3
|
+
// additional HTTP requests or anything you like.
|
4
|
+
//
|
5
|
+
// All the hooks are designed to passthrough a payload. They receive this
|
6
|
+
// payload as the first argument, and a callback function to signal the end of
|
7
|
+
// the hook. You MUST pass the payload as second parameter to the callback, or
|
8
|
+
// pass an error object as first argument. You can modify the payload as you
|
9
|
+
// wish, eg. adding some data from subsequent requests.
|
10
|
+
//
|
11
|
+
// Example hooks:
|
12
|
+
//
|
13
|
+
// // Error case
|
14
|
+
// window.hooks.postCreate.push((payload, cb) => {
|
15
|
+
// cb({ error: true});
|
16
|
+
// });
|
17
|
+
//
|
18
|
+
// // Happy case
|
19
|
+
// window.hooks.postCreate.push((payload, cb) => {
|
20
|
+
// cb(null, Object.assign(payload, { additional: { data: true } }));
|
21
|
+
// });
|
22
|
+
//
|
23
|
+
// Mind the fact that you can define multiple custom functions per hook type.
|
24
|
+
// They are executed after each other in a waterfall like flow. The order of
|
25
|
+
// the hooks array is therefore essential.
|
26
|
+
window.hooks = {
|
27
|
+
// With the help of the +perCreate+ hooks you can manipulate the create
|
28
|
+
// request parameters. Think of an additional handling which reads an
|
29
|
+
// overwrite form or a kind of trait checkboxes to customize the factory
|
30
|
+
// call. The +payload+ looks like this:
|
31
|
+
//
|
32
|
+
// {
|
33
|
+
// factory: 'user',
|
34
|
+
// traits: ['confirmed'],
|
35
|
+
// overwrite: { password: 'secret' }
|
36
|
+
// }
|
37
|
+
preCreate: [],
|
38
|
+
|
39
|
+
// The +postCreate+ hook allows you to perform subsequent requests to fetch
|
40
|
+
// additional data. Think of a user instrumentation where you want to request
|
41
|
+
// a one time token for this user. This token can be added to the payload and
|
42
|
+
// can be shown with the help of the +preCreateResult+ hook. The payload
|
43
|
+
// contains the request parameters and the response body from the
|
44
|
+
// instrumentation request. Here comes an example +payload+:
|
45
|
+
//
|
46
|
+
// {
|
47
|
+
// request: { factory: 'user', /* [..] */ },
|
48
|
+
// response: { /* [..] */ }
|
49
|
+
// }
|
50
|
+
postCreate: [],
|
51
|
+
|
52
|
+
// With the help of the +preCreateResult+ hook you can customize the output
|
53
|
+
// of the result. You could also perform some subsequent requests or some UI
|
54
|
+
// preparations. You can access the output options and the runtime payload
|
55
|
+
// with all its data and make modifications to them. This hook is triggered
|
56
|
+
// before the result is rendered. A sample payload comes here:
|
57
|
+
//
|
58
|
+
// {
|
59
|
+
// alert: 'Your alert text.',
|
60
|
+
// output: 'Formatted response',
|
61
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
62
|
+
// cards: [
|
63
|
+
// `The details accordion card,
|
64
|
+
// you can add more, remove the details card
|
65
|
+
// or reorder them`
|
66
|
+
// ],
|
67
|
+
// openCard: '#details', // Open a custom card, or none
|
68
|
+
// pre: 'Additinal HTML content before the alert.',
|
69
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
70
|
+
// }
|
71
|
+
preCreateResult: [],
|
72
|
+
|
73
|
+
// In case you want to perform some logic after the result is rendered, you
|
74
|
+
// can use the +postCreateResult+ hook. You can access the output options and
|
75
|
+
// the runtime payload with all its data, but changes to them won't take
|
76
|
+
// effect. The +payload+ looks like this:
|
77
|
+
//
|
78
|
+
// {
|
79
|
+
// alert: 'Your alert text.',
|
80
|
+
// output: 'Formatted response',
|
81
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
82
|
+
// cards: [
|
83
|
+
// `The details accordion card,
|
84
|
+
// you can add more, remove the details card
|
85
|
+
// or reorder them`
|
86
|
+
// ],
|
87
|
+
// openCard: '#details', // Open a custom card, or none
|
88
|
+
// pre: 'Additinal HTML content before the alert.',
|
89
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
90
|
+
// }
|
91
|
+
postCreateResult: [],
|
92
|
+
|
93
|
+
// With the help of the +preCreateError+ hook you can customize the output of
|
94
|
+
// the error. Furthermore you can perform some subsequent requests or
|
95
|
+
// whatever comes to your mind. You can access the output options and the
|
96
|
+
// runtime payload with all its data and make modifications to them. This
|
97
|
+
// hook is triggered before the error is rendered. A sample payload comes
|
98
|
+
// here:
|
99
|
+
//
|
100
|
+
// {
|
101
|
+
// alert: 'Your alert text.',
|
102
|
+
// output: 'Formatted response',
|
103
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
104
|
+
// pre: 'Additinal HTML content before the alert.',
|
105
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
106
|
+
// }
|
107
|
+
preCreateError: [],
|
108
|
+
|
109
|
+
// In case you want to perform some magic after an error occured, you can use
|
110
|
+
// the +postCreateError+ hook. You can access the output options and the
|
111
|
+
// runtime payload with all its data, but changes to them won't take effect
|
112
|
+
// because this hook is triggered after the error is rendered. The +payload+
|
113
|
+
// looks like this:
|
114
|
+
//
|
115
|
+
// {
|
116
|
+
// alert: 'Your alert text.',
|
117
|
+
// output: 'Formatted response',
|
118
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
119
|
+
// pre: 'Additinal HTML content before the alert.',
|
120
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
121
|
+
// }
|
122
|
+
postCreateError: []
|
123
|
+
};
|
@@ -0,0 +1,66 @@
|
|
1
|
+
window.Form = Form = function(scope)
|
2
|
+
{
|
3
|
+
this.button = $(scope).find('button');
|
4
|
+
this.result = $('#result');
|
5
|
+
this.spinner = $('#spinner');
|
6
|
+
};
|
7
|
+
|
8
|
+
Form.prototype.bind = function(action)
|
9
|
+
{
|
10
|
+
this.button.on('click', (event) => {
|
11
|
+
event.preventDefault();
|
12
|
+
this.hideResult();
|
13
|
+
action(event);
|
14
|
+
return false;
|
15
|
+
});
|
16
|
+
};
|
17
|
+
|
18
|
+
Form.prototype.hideResult = function()
|
19
|
+
{
|
20
|
+
this.result.hide();
|
21
|
+
this.button.prop('disabled', true);
|
22
|
+
this.spinner.show();
|
23
|
+
};
|
24
|
+
|
25
|
+
Form.prototype.showResultContainer = function(html)
|
26
|
+
{
|
27
|
+
this.result.html(html);
|
28
|
+
$('pre').each((i, block) => hljs.highlightBlock(block));
|
29
|
+
new ClipboardJS('.cb-copy');
|
30
|
+
this.result.show();
|
31
|
+
this.button.prop('disabled', false);
|
32
|
+
};
|
33
|
+
|
34
|
+
Form.prototype.showError = function(payload, output)
|
35
|
+
{
|
36
|
+
this.spinner.hide();
|
37
|
+
output = window.utils.prepareOutput(output);
|
38
|
+
this.errorContent(payload, output, (err, html) => {
|
39
|
+
this.showResultContainer(html);
|
40
|
+
});
|
41
|
+
};
|
42
|
+
|
43
|
+
Form.prototype.errorContent = function(payload, output, cb)
|
44
|
+
{
|
45
|
+
cb(null, `
|
46
|
+
<pre id="data">${output}</pre>
|
47
|
+
${window.utils.clipboardButton()}
|
48
|
+
`);
|
49
|
+
};
|
50
|
+
|
51
|
+
Form.prototype.showResult = function(payload, output)
|
52
|
+
{
|
53
|
+
this.spinner.hide();
|
54
|
+
output = window.utils.prepareOutput(output);
|
55
|
+
this.resultContent(payload, output, (err, html) => {
|
56
|
+
this.showResultContainer(html);
|
57
|
+
});
|
58
|
+
};
|
59
|
+
|
60
|
+
Form.prototype.resultContent = function(payload, output, cb)
|
61
|
+
{
|
62
|
+
cb(null, `
|
63
|
+
<pre id="data">${output}</pre>
|
64
|
+
${window.utils.clipboardButton()}
|
65
|
+
`);
|
66
|
+
};
|
@@ -0,0 +1,116 @@
|
|
1
|
+
window.utils = Utils = {};
|
2
|
+
|
3
|
+
Utils.pushWaterfallPayload = function(data)
|
4
|
+
{
|
5
|
+
return (cb) => cb(null, data);
|
6
|
+
};
|
7
|
+
|
8
|
+
Utils.waterfallWithHooks = function(opts, cb)
|
9
|
+
{
|
10
|
+
cb = cb || function(){};
|
11
|
+
opts = Object.assign({
|
12
|
+
pre: [],
|
13
|
+
post: [],
|
14
|
+
data: {},
|
15
|
+
action: (payload, cb) => cb(null, payload)
|
16
|
+
}, opts);
|
17
|
+
|
18
|
+
async.waterfall([
|
19
|
+
// Yield the data to pre hooks
|
20
|
+
Utils.pushWaterfallPayload(opts.data),
|
21
|
+
// Perform pre hooks
|
22
|
+
...opts.pre,
|
23
|
+
// Perform the create request
|
24
|
+
opts.action,
|
25
|
+
// Perform post hooks
|
26
|
+
...opts.post
|
27
|
+
], cb);
|
28
|
+
};
|
29
|
+
|
30
|
+
Utils.request = function(opts, cb)
|
31
|
+
{
|
32
|
+
opts = Object.assign({
|
33
|
+
url: '/',
|
34
|
+
type: 'POST',
|
35
|
+
data: '{}',
|
36
|
+
dataType: 'json',
|
37
|
+
contentType: 'application/json; charset=utf-8',
|
38
|
+
ignoreErrors: false
|
39
|
+
}, opts || {}, {
|
40
|
+
success: (result) => cb(null, result)
|
41
|
+
});
|
42
|
+
|
43
|
+
errCb = (err) => cb(err);
|
44
|
+
if (opts.ignoreErrors) {
|
45
|
+
errCb = (err) => cb(null, err);
|
46
|
+
}
|
47
|
+
|
48
|
+
$.ajax(opts).fail(errCb);
|
49
|
+
};
|
50
|
+
|
51
|
+
Utils.escape = function(str)
|
52
|
+
{
|
53
|
+
return str.replace(/&/g, "&")
|
54
|
+
.replace(/</g, "<")
|
55
|
+
.replace(/>/g, ">");
|
56
|
+
};
|
57
|
+
|
58
|
+
Utils.prepareOutput = function(output)
|
59
|
+
{
|
60
|
+
try {
|
61
|
+
if (typeof output !== 'object') { output = JSON.parse(output); }
|
62
|
+
output = JSON.stringify(output, null, 2);
|
63
|
+
} catch { }
|
64
|
+
|
65
|
+
return window.utils.escape(output);
|
66
|
+
};
|
67
|
+
|
68
|
+
Utils.clipboardButton = function(id)
|
69
|
+
{
|
70
|
+
id = id || 'data';
|
71
|
+
return `
|
72
|
+
<span class="btn btn-primary cb-copy"
|
73
|
+
data-clipboard-target="#${id}">
|
74
|
+
<i class="fas fa-paste"></i> Copy result to clipboard
|
75
|
+
</span>
|
76
|
+
`;
|
77
|
+
};
|
78
|
+
|
79
|
+
Utils.clipboardBadge = function(id)
|
80
|
+
{
|
81
|
+
id = id || 'data';
|
82
|
+
return `
|
83
|
+
<span class="badge badge-dark cb-copy" title="Copy result to clipboard"
|
84
|
+
data-clipboard-target="#${id}"><i class="fas fa-paste"></i>
|
85
|
+
</span>
|
86
|
+
`;
|
87
|
+
};
|
88
|
+
|
89
|
+
Utils.card = function(opts)
|
90
|
+
{
|
91
|
+
opts = Object.assign({
|
92
|
+
id: 'details',
|
93
|
+
icon: 'fa-asterisk',
|
94
|
+
title: 'Details',
|
95
|
+
body: ''
|
96
|
+
}, opts || {});
|
97
|
+
|
98
|
+
return `
|
99
|
+
<div class="card">
|
100
|
+
<div class="card-header">
|
101
|
+
<h5 class="mb-0">
|
102
|
+
<button class="btn btn-link collapsed" type="button"
|
103
|
+
data-toggle="collapse" data-target="#${opts.id}"
|
104
|
+
aria-expanded="false">
|
105
|
+
<i class="fas ${opts.icon}"></i> ${opts.title}</h5>
|
106
|
+
</button>
|
107
|
+
</h5>
|
108
|
+
</div>
|
109
|
+
<div id="${opts.id}" class="collapse" data-parent="#response">
|
110
|
+
<div class="card-body">
|
111
|
+
${opts.body}
|
112
|
+
</div>
|
113
|
+
</div>
|
114
|
+
</div>
|
115
|
+
`;
|
116
|
+
};
|