@onehat/ui 0.3.283 → 0.3.284
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Components/Editor/Editor.js +0 -1
- package/src/Functions/Cypress/button_functions.js +84 -0
- package/src/Functions/Cypress/common_functions.js +108 -0
- package/src/Functions/Cypress/crud_functions.js +694 -0
- package/src/Functions/Cypress/dom_functions.js +43 -0
- package/src/Functions/Cypress/form_functions.js +510 -0
- package/src/Functions/Cypress/grid_functions.js +269 -0
- package/src/Functions/Cypress/index.js +19 -0
- package/src/Functions/Cypress/navigation_functions.js +77 -0
- package/src/Functions/Cypress/utilities.js +109 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fixInflector,
|
|
3
|
+
getLastPartOfPath,
|
|
4
|
+
} from './utilities';
|
|
5
|
+
import {
|
|
6
|
+
getDomNode,
|
|
7
|
+
getDomNodes,
|
|
8
|
+
} from './dom_functions';
|
|
9
|
+
import Inflector from 'inflector-js';
|
|
10
|
+
import _ from 'lodash';
|
|
11
|
+
const $ = Cypress.$;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// Get rows
|
|
16
|
+
export function hasRowWithFieldValue(gridSelector, field, value) {
|
|
17
|
+
return getDomNodes([gridSelector, 'row', 'cell-' + field]).contains(value);
|
|
18
|
+
}
|
|
19
|
+
export function getRowWithFieldValue(gridSelector, field, value) {
|
|
20
|
+
return getDomNodes([gridSelector, 'row', 'cell-' + field]).contains(value).then((cells) => {
|
|
21
|
+
if (!cells.length) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const cell = cells[0];
|
|
26
|
+
return $(cell).closest('[data-testid="row"]')[0];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// export function getRowWithText(grid, text) {
|
|
30
|
+
// return getRows(grid).contains(text);
|
|
31
|
+
// }
|
|
32
|
+
// export function getRowWithId(grid, id) {
|
|
33
|
+
// return getRows(grid, '[data-cy-recordid=' + id + ']');
|
|
34
|
+
// }
|
|
35
|
+
// export function getRowWithIx(grid, ix) {
|
|
36
|
+
// return getRows(grid, '[data-recordindex=' + ix + ']');
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// Select rows
|
|
41
|
+
export function selectGridRowById(gridSelector, id) {
|
|
42
|
+
cy.then(() => {
|
|
43
|
+
Cypress.log({ name: 'selectGridRowById ' + gridSelector + ' ' + id});
|
|
44
|
+
});
|
|
45
|
+
const rowSelector = getGridRowSelectorById(gridSelector, id);
|
|
46
|
+
getDomNode([gridSelector, rowSelector])
|
|
47
|
+
.click();
|
|
48
|
+
}
|
|
49
|
+
export function selectGridRowIfNotAlreadySelectedById(gridSelector, id) {
|
|
50
|
+
cy.then(() => {
|
|
51
|
+
Cypress.log({ name: 'selectGridRowIfNotAlreadySelectedById ' + gridSelector + ' ' + id});
|
|
52
|
+
});
|
|
53
|
+
const rowSelector = getGridRowSelectorById(gridSelector, id);
|
|
54
|
+
getDomNode([gridSelector, rowSelector]).then((row) => {
|
|
55
|
+
const found = row.find('[data-testid="row-selected"]')
|
|
56
|
+
if (!found.length) {
|
|
57
|
+
selectGridRowById(gridSelector, id);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
// export function selectRowWithText(grid, text) {
|
|
62
|
+
// getRowWithText(grid, text).click(5, 5);
|
|
63
|
+
// }
|
|
64
|
+
// export function selectRowWithIx(grid, ix) {
|
|
65
|
+
// getRowWithIx(grid, ix).click(5, 5);
|
|
66
|
+
// }
|
|
67
|
+
// export function cmdClickRowWithId(grid, id) {
|
|
68
|
+
// getRowWithId(grid, id).click('left', { metaKey: true });
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
// // Double-click rows
|
|
73
|
+
// export function doubleClickRowWithText(grid, text) {
|
|
74
|
+
// getRowWithText(grid, text).dblclick();
|
|
75
|
+
// }
|
|
76
|
+
// export function doubleClickRowWithId(grid, id) {
|
|
77
|
+
// getRowWithId(grid, id).dblclick();
|
|
78
|
+
// }
|
|
79
|
+
// export function doubleClickRowWithIx(grid, ix) {
|
|
80
|
+
// getRowWithIx(grid, ix).dblclick();
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
export function verifyGridRecordDoesNotExistByValue(gridSelector, fieldValues, schema) {
|
|
85
|
+
const
|
|
86
|
+
field = schema.model.displayProperty,
|
|
87
|
+
value = fieldValues[field];
|
|
88
|
+
|
|
89
|
+
getDomNodes([gridSelector, 'row', 'cell-' + field])
|
|
90
|
+
.contains(value, { timeout: 500 })
|
|
91
|
+
.should('not.exist');
|
|
92
|
+
}
|
|
93
|
+
export function verifyGridRecordExistsByValue(gridSelector, fieldValues, schema) {
|
|
94
|
+
const
|
|
95
|
+
field = schema.model.displayProperty,
|
|
96
|
+
value = fieldValues[field];
|
|
97
|
+
|
|
98
|
+
getDomNodes([gridSelector, 'row', 'cell-' + field])
|
|
99
|
+
.contains(value, { timeout: 500 })
|
|
100
|
+
.should('exist');
|
|
101
|
+
}
|
|
102
|
+
export function verifyGridRecordExistsById(gridSelector, id) {
|
|
103
|
+
cy.then(() => {
|
|
104
|
+
Cypress.log({ name: 'verifyGridRecordExistsById ' + gridSelector + ' ' + id });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const rowSelector = getGridRowSelectorById(gridSelector, id);
|
|
108
|
+
getDomNodes([gridSelector, rowSelector])
|
|
109
|
+
.should('exist');
|
|
110
|
+
}
|
|
111
|
+
export function verifyGridRecordDoesNotExistById(gridSelector, id) {
|
|
112
|
+
cy.then(() => {
|
|
113
|
+
Cypress.log({ name: 'verifyGridRecordDoesNotExistById ' + gridSelector + ' ' + id });
|
|
114
|
+
});
|
|
115
|
+
const rowSelector = getGridRowSelectorById(gridSelector, id);
|
|
116
|
+
getDomNodes([gridSelector, rowSelector])
|
|
117
|
+
.should('not.exist');
|
|
118
|
+
}
|
|
119
|
+
export function verifyGridRowIsSelectedById(gridSelector, id) {
|
|
120
|
+
cy.then(() => {
|
|
121
|
+
Cypress.log({ name: 'verifyGridRowIsSelectedById ' + gridSelector + ' ' + id});
|
|
122
|
+
});
|
|
123
|
+
const rowSelector = getGridRowSelectorById(gridSelector, id);
|
|
124
|
+
getDomNodes([gridSelector, rowSelector, 'row-selected'])
|
|
125
|
+
.should('exist');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
// export function addRecordWithInlineEditor(grid, fieldValues, schema) {
|
|
133
|
+
// clickGridAddButton(grid, true);
|
|
134
|
+
// fillInlineForm(grid, fieldValues, schema);
|
|
135
|
+
// cy.route('POST', '**/extAdd**').as('addWaiter');
|
|
136
|
+
// submitInlineForm(grid);
|
|
137
|
+
// cy.wait('@addWaiter');
|
|
138
|
+
// verifyNoErrorBox();
|
|
139
|
+
// getSelectedRowId(grid); // Adds @id alias
|
|
140
|
+
// cy.wait(1000);
|
|
141
|
+
// }
|
|
142
|
+
// export function addRecordWithWindowedEditor(grid, editorCls, fieldValues, schema) {
|
|
143
|
+
// clickGridAddButton(grid, true);
|
|
144
|
+
// fillWindowedForm(editorCls, fieldValues, schema);
|
|
145
|
+
// cy.route('POST', '**/extAdd**').as('addWaiter');
|
|
146
|
+
// submitWindowedForm(editorCls);
|
|
147
|
+
// cy.wait('@addWaiter');
|
|
148
|
+
// verifyNoErrorBox();
|
|
149
|
+
// getSelectedRowId(grid); // Adds @id alias
|
|
150
|
+
// cy.wait(1000);
|
|
151
|
+
// }
|
|
152
|
+
// export function editRecordWithInlineEditor(grid, fieldValues, schema) {
|
|
153
|
+
// cy.get("@id").then((id) => {
|
|
154
|
+
// doubleClickRowWithId(grid, id);
|
|
155
|
+
// fillInlineForm(grid, fieldValues, schema);
|
|
156
|
+
// cy.route('POST', '**/extEdit**').as('editWaiter');
|
|
157
|
+
// submitInlineForm(grid);
|
|
158
|
+
// cy.wait('@editWaiter');
|
|
159
|
+
// verifyNoErrorBox();
|
|
160
|
+
// cy.wait(500);
|
|
161
|
+
// });
|
|
162
|
+
// }
|
|
163
|
+
// export function editRecordWithWindowedEditor(grid, editorCls, fieldValues, schema) {
|
|
164
|
+
// cy.get("@id").then((id) => {
|
|
165
|
+
// doubleClickRowWithId(grid, id);
|
|
166
|
+
// fillWindowedForm(editorCls, fieldValues, schema);
|
|
167
|
+
// cy.route('POST', '**/extEdit**').as('editWaiter');
|
|
168
|
+
// submitWindowedForm(editorCls);
|
|
169
|
+
// cy.wait('@editWaiter');
|
|
170
|
+
// verifyNoErrorBox();
|
|
171
|
+
// });
|
|
172
|
+
// }
|
|
173
|
+
// export function removeRecord(grid) {
|
|
174
|
+
// cy.get("@id").then((id) => {
|
|
175
|
+
// cy.wait(500);
|
|
176
|
+
// clickGridRemoveButton(grid);
|
|
177
|
+
// cy.route('POST', '**/extDelete**').as('removeWaiter');
|
|
178
|
+
// clickMessageBoxDefaultButton();
|
|
179
|
+
// cy.wait('@removeWaiter');
|
|
180
|
+
// verifyNoErrorBox();
|
|
181
|
+
// });
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
// Grid Utilities
|
|
186
|
+
export function getModelFromGridName(gridName) {
|
|
187
|
+
// try to match with something like 'EquipmentFilteredGridEditor'
|
|
188
|
+
const
|
|
189
|
+
pattern = '^' + // start
|
|
190
|
+
'([\\w]+?)' + // model name
|
|
191
|
+
'(?=Filtered|Inline|Side|Grid)' + // positive lookahead to guarantee one of these is the next word
|
|
192
|
+
'(Filtered)?' + // optional
|
|
193
|
+
'(Inline)?' + // optional
|
|
194
|
+
'(Side)?' + // optional
|
|
195
|
+
'Grid' + // required
|
|
196
|
+
'(Editor)?' + // optional
|
|
197
|
+
'(Side[AB])?' + // optional
|
|
198
|
+
'$', // end
|
|
199
|
+
regex = new RegExp(pattern),
|
|
200
|
+
match = gridName.match(regex);
|
|
201
|
+
return match?.length ? match[1] : null;
|
|
202
|
+
}
|
|
203
|
+
export function getModelFromGridPath(gridPath) {
|
|
204
|
+
// try to match with something like '...__eq_manufacturer_id/grid'
|
|
205
|
+
let
|
|
206
|
+
pattern = '^' + // start
|
|
207
|
+
'.*' + // previous selector path (ignore)
|
|
208
|
+
'(?=__)' + // positive lookahead to guarantee '__' is the next word
|
|
209
|
+
'__' + // required
|
|
210
|
+
'([A-Za-z_]+)' + // field name (model name underscored, singularized)
|
|
211
|
+
'([\\d]+)?' + // optional (digit, e.g. eq_engine_model1_id)
|
|
212
|
+
'_id/(combo/)?grid' + // required
|
|
213
|
+
'$', // end
|
|
214
|
+
regex = new RegExp(pattern),
|
|
215
|
+
match = gridPath.match(regex);
|
|
216
|
+
if (!match) {
|
|
217
|
+
// try to match with something like '.../work_orders__equipment/combo/grid"'
|
|
218
|
+
pattern = '^' + // start
|
|
219
|
+
'.*' + // previous selector path (ignore)
|
|
220
|
+
'(?=__)' + // positive lookahead to guarantee '__' is the next word
|
|
221
|
+
'__' + // required
|
|
222
|
+
'([A-Za-z_]+)' + // field name (model name underscored, pluralized)
|
|
223
|
+
'/(combo/)?grid' + // required
|
|
224
|
+
'$', // end
|
|
225
|
+
regex = new RegExp(pattern);
|
|
226
|
+
match = gridPath.match(regex);
|
|
227
|
+
}
|
|
228
|
+
return match?.length ? match[1] : null;
|
|
229
|
+
}
|
|
230
|
+
export function getModelFromGridSelector(gridSelector) {
|
|
231
|
+
const gridName = getLastPartOfPath(gridSelector);
|
|
232
|
+
let model = getModelFromGridName(gridName);
|
|
233
|
+
if (!model) {
|
|
234
|
+
model = getModelFromGridPath(gridSelector);
|
|
235
|
+
if (model) {
|
|
236
|
+
model = fixInflector(Inflector.camelize(Inflector.pluralize(model)));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return model;
|
|
240
|
+
}
|
|
241
|
+
export function getGridRowSelectorById(gridSelector, id) {
|
|
242
|
+
const
|
|
243
|
+
model = getModelFromGridSelector(gridSelector);
|
|
244
|
+
|
|
245
|
+
if (!model) {
|
|
246
|
+
debugger;
|
|
247
|
+
}
|
|
248
|
+
const inflected = fixInflector(Inflector.camelize(Inflector.pluralize(model)));
|
|
249
|
+
return inflected + '-' + id;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
// function fillInlineForm(grid, fieldValues, schema) {
|
|
255
|
+
// getInlineEditor(grid)
|
|
256
|
+
// .then((editor) => {
|
|
257
|
+
// fillForm(editor, fieldValues, schema);
|
|
258
|
+
// });
|
|
259
|
+
// }
|
|
260
|
+
// function submitInlineForm(grid) {
|
|
261
|
+
// cy.wait(1000);
|
|
262
|
+
// getInlineEditorButtons(grid)
|
|
263
|
+
// .filter('.x-row-editor-update-button')
|
|
264
|
+
// // .then((el) => {
|
|
265
|
+
// // cy.get(el[0]).click();
|
|
266
|
+
// // // el[0].click();
|
|
267
|
+
// // });
|
|
268
|
+
// .click({ force: true });
|
|
269
|
+
// }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as ButtonFunctions from './button_functions.js';
|
|
2
|
+
import * as CommonFunctions from './common_functions.js';
|
|
3
|
+
import * as CrudFunctions from './crud_functions.js';
|
|
4
|
+
import * as DomFunctions from './dom_functions.js';
|
|
5
|
+
import * as FormFunctions from './form_functions.js';
|
|
6
|
+
import * as GridFunctions from './grid_functions.js';
|
|
7
|
+
import * as NavigationFunctions from './navigation_functions.js';
|
|
8
|
+
import * as Utilities from './utilities.js';
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
...ButtonFunctions,
|
|
12
|
+
...CommonFunctions,
|
|
13
|
+
...CrudFunctions,
|
|
14
|
+
...DomFunctions,
|
|
15
|
+
...FormFunctions,
|
|
16
|
+
...GridFunctions,
|
|
17
|
+
...NavigationFunctions,
|
|
18
|
+
...Utilities,
|
|
19
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DOMAIN,
|
|
3
|
+
LOGIN_ID_FIELD,
|
|
4
|
+
SUPER_USER,
|
|
5
|
+
SUPER_EMAIL,
|
|
6
|
+
SUPER_PASSWORD,
|
|
7
|
+
} from './constants';
|
|
8
|
+
import {
|
|
9
|
+
getDomNode,
|
|
10
|
+
getDomNodes,
|
|
11
|
+
} from './dom_functions';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// __ _
|
|
15
|
+
// / / ____ ____ _(_)___
|
|
16
|
+
// / / / __ \/ __ `/ / __ \
|
|
17
|
+
// / /___/ /_/ / /_/ / / / / /
|
|
18
|
+
// /_____/\____/\__, /_/_/ /_/
|
|
19
|
+
// /____/
|
|
20
|
+
|
|
21
|
+
export function login(user, password) {
|
|
22
|
+
cy.visit(Cypress.env('baseUrl') + 'login')
|
|
23
|
+
.then(() => {
|
|
24
|
+
getDomNode(LOGIN_ID_FIELD).clear();
|
|
25
|
+
getDomNode(LOGIN_ID_FIELD).type(user);
|
|
26
|
+
|
|
27
|
+
getDomNode('password').clear();
|
|
28
|
+
getDomNode('password').type(password);
|
|
29
|
+
|
|
30
|
+
getDomNode('loginBtn').click();
|
|
31
|
+
cy.url().should('include', 'home');
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function loginAsSuper() {
|
|
35
|
+
return login(LOGIN_ID_FIELD === 'username' ? SUPER_USER : SUPER_EMAIL, SUPER_PASSWORD);
|
|
36
|
+
}
|
|
37
|
+
export function logout() {
|
|
38
|
+
getDomNode('/logout').click({ force: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// _ __ _ __ _
|
|
43
|
+
// / | / /___ __ __(_)___ _____ _/ /_(_)___ ____
|
|
44
|
+
// / |/ / __ `/ | / / / __ `/ __ `/ __/ / __ \/ __ \
|
|
45
|
+
// / /| / /_/ /| |/ / / /_/ / /_/ / /_/ / /_/ / / / /
|
|
46
|
+
// /_/ |_/\__,_/ |___/_/\__, /\__,_/\__/_/\____/_/ /_/
|
|
47
|
+
// /____/
|
|
48
|
+
|
|
49
|
+
export function navigateViaTabOrHomeButtonTo(url) {
|
|
50
|
+
// i.e. If we're on home screen, press the button.
|
|
51
|
+
// If we have a tab navigation, press the tab's button
|
|
52
|
+
getDomNode(url).click(); // i.e. the DomNode's data-testid is the url
|
|
53
|
+
cy.url().should('include', url);
|
|
54
|
+
}
|
|
55
|
+
export function navigateToHome() {
|
|
56
|
+
navigateToScreen('/home');
|
|
57
|
+
}
|
|
58
|
+
export function navigateToScreen(path) {
|
|
59
|
+
cy.visit(DOMAIN + path)
|
|
60
|
+
.then(() => {
|
|
61
|
+
cy.url().should('include', path);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// export function selectMainTab(name) {
|
|
65
|
+
// cy.get('.mainTabPanel .x-tab')
|
|
66
|
+
// .contains(name)
|
|
67
|
+
// .first()
|
|
68
|
+
// .click();
|
|
69
|
+
// cy.wait(1000); // Time to render new tab
|
|
70
|
+
// }
|
|
71
|
+
// export function selectSecondaryTab(name) {
|
|
72
|
+
// cy.get('.mainTabPanel > .x-panel-bodyWrap > .x-panel-body .x-tab')
|
|
73
|
+
// .contains(name)
|
|
74
|
+
// .first()
|
|
75
|
+
// .click();
|
|
76
|
+
// cy.wait(1000); // Time to render new tab
|
|
77
|
+
// }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
|
|
3
|
+
// __ ____ _ ___ __ _
|
|
4
|
+
// / / / / /_(_) (_) /_(_)__ _____
|
|
5
|
+
// / / / / __/ / / / __/ / _ \/ ___/
|
|
6
|
+
// / /_/ / /_/ / / / /_/ / __(__ )
|
|
7
|
+
// \____/\__/_/_/_/\__/_/\___/____/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export function fixInflector(str) {
|
|
11
|
+
// inflector-js doesn't handle pluralization of 'equipment' correctly
|
|
12
|
+
str = str.replace(/quipments/, 'quipment');
|
|
13
|
+
return str;
|
|
14
|
+
}
|
|
15
|
+
export function getPropertyDefinitionFromSchema(fieldName, schema) {
|
|
16
|
+
return _.find(schema.model.properties, { name: fieldName });
|
|
17
|
+
}
|
|
18
|
+
export function getLastPartOfPath(path) {
|
|
19
|
+
return path.split('/').pop();
|
|
20
|
+
}
|
|
21
|
+
export function unescapeHtml(html) {
|
|
22
|
+
const el = document.createElement('div');
|
|
23
|
+
return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
|
|
24
|
+
el.innerHTML = enc;
|
|
25
|
+
return el.innerText
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Given a URL query string,
|
|
31
|
+
* return an object with keyed properties
|
|
32
|
+
* @param {string} urlString
|
|
33
|
+
* @return {object} keyed properties
|
|
34
|
+
*/
|
|
35
|
+
export function extractKeysValuesFromQueryString(urlString) {
|
|
36
|
+
var entries = (new URLSearchParams(urlString)).entries(),
|
|
37
|
+
fv = {},
|
|
38
|
+
entry;
|
|
39
|
+
while(!(entry = entries.next()).done) {
|
|
40
|
+
let [key, val] = entry.value;
|
|
41
|
+
// if (/\[\]$/.test(key)) {
|
|
42
|
+
// key = key.slice(0, -2);
|
|
43
|
+
// }
|
|
44
|
+
if (fv.hasOwnProperty(key)) {
|
|
45
|
+
if (!_.isArray(fv[key])) {
|
|
46
|
+
var tmp = fv[key];
|
|
47
|
+
fv[key] = [];
|
|
48
|
+
fv[key].push(tmp);
|
|
49
|
+
}
|
|
50
|
+
fv[key].push(val);
|
|
51
|
+
} else {
|
|
52
|
+
fv[key] = val;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return fv;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* JS equivalent of PHP's urlencode
|
|
60
|
+
* https://www.php.net/manual/en/function.urlencode.php
|
|
61
|
+
* Author: http://kevin.vanzonneveld.net
|
|
62
|
+
* @param {string} string to encode
|
|
63
|
+
*/
|
|
64
|
+
export function urlencode(str) {
|
|
65
|
+
// Note: Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
|
|
66
|
+
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Deep diff between two objects - i.e. an object with the new value of new & changed fields.
|
|
71
|
+
* Removed fields will be set as undefined on the result.
|
|
72
|
+
* Only plain objects will be deeply compared (@see _.isPlainObject)
|
|
73
|
+
*
|
|
74
|
+
* Inspired by: https://gist.github.com/Yimiprod/7ee176597fef230d1451#gistcomment-2565071
|
|
75
|
+
* This fork: https://gist.github.com/TeNNoX/5125ab5770ba287012316dd62231b764/
|
|
76
|
+
*
|
|
77
|
+
* @param {Object} base Object to compare with (if falsy we return object)
|
|
78
|
+
* @param {Object} object Object compared
|
|
79
|
+
* @return {Object} Return a new object who represent the changed & new values
|
|
80
|
+
*/
|
|
81
|
+
export function deepDiffObj(base, object) {
|
|
82
|
+
if (!object) throw new Error(`The object compared should be an object: ${object}`);
|
|
83
|
+
if (!base) return object;
|
|
84
|
+
const result = _.transform(object, (result, value, key) => {
|
|
85
|
+
if (!_.has(base, key)) result[key] = value; // fix edge case: not defined to explicitly defined as undefined
|
|
86
|
+
if (!_.isEqual(value, base[key])) {
|
|
87
|
+
result[key] = _.isPlainObject(value) && _.isPlainObject(base[key]) ? deepDiffObj(base[key], value) : value;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// map removed fields to undefined
|
|
91
|
+
_.forOwn(base, (value, key) => {
|
|
92
|
+
if (!_.has(object, key)) result[key] = undefined;
|
|
93
|
+
});
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const dates = {
|
|
98
|
+
today: moment().format('YYYY-MM-DD'),
|
|
99
|
+
todayDatetime: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
100
|
+
yesterday: moment().subtract(1, 'days').format('YYYY-MM-DD'),
|
|
101
|
+
tomorrow: moment().add(1, 'days').format('YYYY-MM-DD'),
|
|
102
|
+
oneMonthAgo: moment().subtract(1, 'months').format('YYYY-MM-DD'),
|
|
103
|
+
oneYearAgo: moment().subtract(1, 'years').format('YYYY-MM-DD'),
|
|
104
|
+
startOfThisMonth: moment().startOf('months').format('YYYY-MM-DD'),
|
|
105
|
+
endOfLastMonth: moment().subtract(1, 'months').endOf('month').format('YYYY-MM-DD'),
|
|
106
|
+
twoMonthsAgo: moment().subtract(2, 'months').format('YYYY-MM-DD'),
|
|
107
|
+
sixMonthsAgo: moment().subtract(6, 'months').format('YYYY-MM-DD'),
|
|
108
|
+
oneMonthFromNow: moment().add(1, 'months').format('YYYY-MM-DD'),
|
|
109
|
+
};
|