@onehat/ui 0.4.33 → 0.4.35

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.
@@ -13,6 +13,7 @@ import {
13
13
  EDITOR_TYPE__SIDE,
14
14
  EDITOR_TYPE__INLINE,
15
15
  } from '../../Constants/Editor.js';
16
+ import useForceUpdate from '../../Hooks/useForceUpdate.js'
16
17
  import Button from '../Buttons/Button.js';
17
18
  import UiGlobals from '../../UiGlobals.js';
18
19
  import _ from 'lodash';
@@ -24,7 +25,6 @@ export default function withEditor(WrappedComponent, isTree = false) {
24
25
  return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
25
26
  }
26
27
 
27
- let [editorMode, setEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
28
28
  const {
29
29
  userCanEdit = true, // not permissions, but capability
30
30
  userCanView = true,
@@ -69,6 +69,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
69
69
 
70
70
  // withSelection
71
71
  selection,
72
+ getSelection,
72
73
  setSelection,
73
74
 
74
75
  // withAlert
@@ -76,16 +77,24 @@ export default function withEditor(WrappedComponent, isTree = false) {
76
77
  confirm,
77
78
  hideAlert,
78
79
  } = props,
80
+ forceUpdate = useForceUpdate(),
79
81
  listeners = useRef({}),
80
82
  editorStateRef = useRef(),
81
83
  newEntityDisplayValueRef = useRef(),
84
+ editorModeRef = useRef(EDITOR_MODE__VIEW),
85
+ isIgnoreNextSelectionChangeRef = useRef(false),
82
86
  [currentRecord, setCurrentRecord] = useState(null),
83
87
  [isAdding, setIsAdding] = useState(false),
84
88
  [isSaving, setIsSaving] = useState(false),
85
89
  [isEditorShown, setIsEditorShownRaw] = useState(false),
86
90
  [isEditorViewOnly, setIsEditorViewOnly] = useState(canEditorViewOnly), // current state of whether editor is in view-only mode
87
- [isIgnoreNextSelectionChange, setIsIgnoreNextSelectionChange] = useState(false),
88
91
  [lastSelection, setLastSelection] = useState(),
92
+ setIsIgnoreNextSelectionChange = (bool) => {
93
+ isIgnoreNextSelectionChangeRef.current = bool;
94
+ },
95
+ getIsIgnoreNextSelectionChange = () => {
96
+ return isIgnoreNextSelectionChangeRef.current;
97
+ },
89
98
  setIsEditorShown = (bool) => {
90
99
  setIsEditorShownRaw(bool);
91
100
  if (!bool && onEditorClose) {
@@ -96,8 +105,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
96
105
  function doIt() {
97
106
  setSelection(newSelection);
98
107
  }
99
- const formState = editorStateRef.current;
100
- if (!_.isEmpty(formState?.dirtyFields) && newSelection !== selection && editorMode === EDITOR_MODE__EDIT) {
108
+ const
109
+ formState = editorStateRef.current,
110
+ selection = getSelection();
111
+ if (!_.isEmpty(formState?.dirtyFields) && newSelection !== selection && getEditorMode() === EDITOR_MODE__EDIT) {
101
112
  confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
102
113
  } else if (selection && selection[0] && !selection[0].isDestroyed && (selection[0]?.isPhantom || selection[0]?.isRemotePhantom)) {
103
114
  confirm('This new record is unsaved. Are you sure you want to cancel editing? Changes will be lost.', async () => {
@@ -115,6 +126,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
115
126
  listeners.current = obj;
116
127
  // forceUpdate(); // we don't want to get into an infinite loop of renders. Simply directly assign the listeners in every child render
117
128
  },
129
+ getEditorMode = () => {
130
+ return editorModeRef.current;
131
+ },
132
+ setEditorMode = (mode) => {
133
+ if (editorModeRef.current !== mode) {
134
+ editorModeRef.current = mode;
135
+ forceUpdate();
136
+ }
137
+ },
118
138
  getNewEntityDisplayValue = () => {
119
139
  return newEntityDisplayValueRef.current;
120
140
  },
@@ -124,6 +144,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
124
144
  return;
125
145
  }
126
146
 
147
+ const selection = getSelection();
127
148
  let addValues = values;
128
149
 
129
150
  if (Repository?.isLoading) {
@@ -221,6 +242,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
221
242
  showPermissionsError(EDIT);
222
243
  return;
223
244
  }
245
+ const selection = getSelection();
224
246
  if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
225
247
  return;
226
248
  }
@@ -243,6 +265,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
243
265
  if (_.isFunction(args)) {
244
266
  cb = args;
245
267
  }
268
+ const selection = getSelection();
246
269
  if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
247
270
  return;
248
271
  }
@@ -301,6 +324,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
301
324
  showPermissionsError(DELETE);
302
325
  return;
303
326
  }
327
+ const selection = getSelection();
304
328
  if (getListeners().onBeforeDelete) {
305
329
  const listenerResult = await getListeners().onBeforeDelete(selection);
306
330
  if (listenerResult === false) {
@@ -341,6 +365,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
341
365
 
342
366
  // check permissions for view
343
367
 
368
+ const selection = getSelection();
344
369
  if (selection.length !== 1) {
345
370
  return;
346
371
  }
@@ -363,7 +388,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
363
388
 
364
389
  // check permissions for duplicate
365
390
 
366
-
391
+ const selection = getSelection();
367
392
  if (selection.length !== 1) {
368
393
  return;
369
394
  }
@@ -384,6 +409,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
384
409
  },
385
410
  onRemoteDuplicate = async () => {
386
411
  const
412
+ selection = getSelection(),
387
413
  entity = selection[0],
388
414
  duplicateEntity = await Repository.remoteDuplicate(entity);
389
415
 
@@ -392,14 +418,16 @@ export default function withEditor(WrappedComponent, isTree = false) {
392
418
  doEdit();
393
419
  },
394
420
  doEditorSave = async (data, e) => {
395
- let mode = editorMode === EDITOR_MODE__ADD ? ADD : EDIT;
421
+ let mode = getEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
396
422
  if (canUser && !canUser(mode)) {
397
423
  showPermissionsError(mode);
398
424
  return;
399
425
  }
400
426
 
401
427
  // NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
402
- const isSingle = selection.length === 1;
428
+ const
429
+ selection = getSelection(),
430
+ isSingle = selection.length === 1;
403
431
  let useStaged = false;
404
432
  if (isSingle) {
405
433
  // just update this one entity
@@ -446,7 +474,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
446
474
  if (onChange) {
447
475
  onChange(selection);
448
476
  }
449
- if (editorMode === EDITOR_MODE__ADD) {
477
+ if (getEditorMode() === EDITOR_MODE__ADD) {
450
478
  if (onAdd) {
451
479
  await onAdd(selection);
452
480
  }
@@ -459,7 +487,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
459
487
  } else {
460
488
  setEditorMode(EDITOR_MODE__VIEW);
461
489
  }
462
- } else if (editorMode === EDITOR_MODE__EDIT) {
490
+ } else if (getEditorMode() === EDITOR_MODE__EDIT) {
463
491
  if (getListeners().onAfterEdit) {
464
492
  await getListeners().onAfterEdit(selection);
465
493
  }
@@ -475,6 +503,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
475
503
  doEditorCancel = () => {
476
504
  async function doIt() {
477
505
  const
506
+ selection = getSelection(),
478
507
  isSingle = selection.length === 1,
479
508
  isPhantom = selection[0] && !selection[0]?.isDestroyed && selection[0].isPhantom;
480
509
  if (isSingle && isPhantom) {
@@ -513,9 +542,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
513
542
  setIsEditorShown(false);
514
543
  });
515
544
  },
516
- calculateEditorMode = (isIgnoreNextSelectionChange = false) => {
545
+ calculateEditorMode = () => {
517
546
 
518
- let doStayInEditModeOnSelectionChange = stayInEditModeOnSelectionChange;
547
+ let isIgnoreNextSelectionChange = getIsIgnoreNextSelectionChange(),
548
+ doStayInEditModeOnSelectionChange = stayInEditModeOnSelectionChange;
519
549
  if (!_.isNil(UiGlobals.stayInEditModeOnSelectionChange)) {
520
550
  // allow global override to for this property
521
551
  doStayInEditModeOnSelectionChange = UiGlobals.stayInEditModeOnSelectionChange;
@@ -525,13 +555,14 @@ export default function withEditor(WrappedComponent, isTree = false) {
525
555
  }
526
556
 
527
557
  // calculateEditorMode gets called only on selection changes
558
+ const selection = getSelection();
528
559
  let mode;
529
560
  if (editorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
530
561
  // special case: side editor is always edit mode
531
562
  mode = EDITOR_MODE__EDIT;
532
563
  } else {
533
564
  if (isIgnoreNextSelectionChange) {
534
- mode = editorMode;
565
+ mode = getEditorMode();
535
566
  if (!canEditorViewOnly && userCanEdit) {
536
567
  if (selection.length > 1) {
537
568
  if (!disableEdit) {
@@ -577,7 +608,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
577
608
  };
578
609
 
579
610
  useEffect(() => {
580
- setEditorMode(calculateEditorMode(isIgnoreNextSelectionChange));
611
+ setEditorMode(calculateEditorMode());
581
612
 
582
613
  setIsIgnoreNextSelectionChange(false);
583
614
  setLastSelection(selection);
@@ -598,7 +629,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
598
629
  // NOTE: If I don't calculate this on the fly for selection changes,
599
630
  // we see a flash of the previous state, since useEffect hasn't yet run.
600
631
  // (basically redo what's in the useEffect, above)
601
- editorMode = calculateEditorMode(isIgnoreNextSelectionChange);
632
+ setEditorMode(calculateEditorMode());
602
633
  }
603
634
 
604
635
  return <WrappedComponent
@@ -611,11 +642,13 @@ export default function withEditor(WrappedComponent, isTree = false) {
611
642
  isEditorViewOnly={isEditorViewOnly}
612
643
  isAdding={isAdding}
613
644
  isSaving={isSaving}
614
- editorMode={editorMode}
645
+ editorMode={getEditorMode()}
646
+ getEditorMode={getEditorMode}
615
647
  onEditMode={setEditMode}
616
648
  onViewMode={setViewMode}
617
649
  editorStateRef={editorStateRef}
618
650
  setIsEditorShown={setIsEditorShown}
651
+ setIsIgnoreNextSelectionChange={setIsIgnoreNextSelectionChange}
619
652
  onAdd={(!userCanEdit || disableAdd) ? null : doAdd}
620
653
  onEdit={(!userCanEdit || disableEdit) ? null : doEdit}
621
654
  onDelete={(!userCanEdit || disableDelete) ? null : doDelete}
@@ -206,6 +206,8 @@ export default function withPdfButtons(WrappedComponent) {
206
206
  h: 800,
207
207
  w: styles.FORM_STACK_ROW_THRESHOLD + 10,
208
208
  body: <Form
209
+ parent={self}
210
+ reference="chooseFieldsForm"
209
211
  editorType={EDITOR_TYPE__PLAIN}
210
212
  alert={alert}
211
213
  columnDefaults={{
@@ -244,6 +246,8 @@ export default function withPdfButtons(WrappedComponent) {
244
246
  w: 510, // 510 so it's over the stack threshold
245
247
  h: 500,
246
248
  body: <Form
249
+ parent={self}
250
+ reference="chooseEmailAddressForm"
247
251
  submitBtnLabel='Email PDF'
248
252
  onSubmit={(values)=> {
249
253
  hideModal();
@@ -23,8 +23,7 @@ export default function withSelection(WrappedComponent) {
23
23
  return <WrappedComponent {...props} ref={ref} />;
24
24
  }
25
25
 
26
- const
27
- {
26
+ const {
28
27
  selection,
29
28
  defaultSelection,
30
29
  onChangeSelection,
@@ -48,14 +47,14 @@ export default function withSelection(WrappedComponent) {
48
47
  usesWithValue = !!setValue,
49
48
  initialSelection = selection || defaultSelection || [],
50
49
  forceUpdate = useForceUpdate(),
51
- localSelection = useRef(initialSelection),
50
+ selectionRef = useRef(initialSelection),
52
51
  [isReady, setIsReady] = useState(selection || false), // if selection is already defined, or value is not null and we don't need to load repository, it's ready
53
52
  setSelection = (selection) => {
54
- if (_.isEqual(selection, localSelection.current)) {
53
+ if (_.isEqual(selection, getSelection())) {
55
54
  return;
56
55
  }
57
56
 
58
- localSelection.current = selection;
57
+ selectionRef.current = selection;
59
58
  if (onChangeSelection) {
60
59
  onChangeSelection(selection);
61
60
  }
@@ -64,6 +63,9 @@ export default function withSelection(WrappedComponent) {
64
63
  }
65
64
  forceUpdate();
66
65
  },
66
+ getSelection = () => {
67
+ return selectionRef.current;
68
+ }
67
69
  selectPrev = () => {
68
70
  selectDirection(SELECT_UP);
69
71
  },
@@ -103,21 +105,23 @@ export default function withSelection(WrappedComponent) {
103
105
  }
104
106
  },
105
107
  addToSelection = (item) => {
106
- const newSelection = _.clone(localSelection.current); // so we get a new object, so descendants rerender
108
+ const newSelection = _.clone(getSelection()); // so we get a new object, so descendants rerender
107
109
  newSelection.push(item);
108
110
  setSelection(newSelection);
109
111
  },
110
112
  removeFromSelection = (item) => {
111
113
  let newSelection = [];
112
114
  if (Repository) {
113
- newSelection = _.remove(localSelection.current, (sel) => sel !== item);
115
+ newSelection = _.remove(getSelection(), (sel) => sel !== item);
114
116
  } else {
115
- newSelection = _.remove(localSelection.current, (sel) => sel[idIx] !== item[idIx]);
117
+ newSelection = _.remove(getSelection(), (sel) => sel[idIx] !== item[idIx]);
116
118
  }
117
119
  setSelection(newSelection);
118
120
  },
119
121
  deselectAll = () => {
120
- setSelection([]);
122
+ if (!_.isEmpty(getSelection())) {
123
+ setSelection([]);
124
+ }
121
125
  },
122
126
  refreshSelection = () => {
123
127
  // When Repository reloads, the entities get destroyed.
@@ -126,7 +130,7 @@ export default function withSelection(WrappedComponent) {
126
130
  // That way, after a load event, we'll keep the same selection, if possible.
127
131
  const
128
132
  newSelection = [],
129
- ids = _.map(localSelection.current, (item) => item.id);
133
+ ids = _.map(getSelection(), (item) => item.id);
130
134
  _.each(ids, (id) => {
131
135
  const found = Repository.getById(id);
132
136
  if (found) {
@@ -160,9 +164,9 @@ export default function withSelection(WrappedComponent) {
160
164
  selectRangeTo = (item) => {
161
165
  // Select above max or below min to this one
162
166
  const
163
- currentSelectionLength = localSelection.current.length,
167
+ currentSelectionLength = getSelection().length,
164
168
  index = getIndexOfSelectedItem(item);
165
- let newSelection = _.clone(localSelection.current); // so we get a new object, so descendants rerender
169
+ let newSelection = _.clone(getSelection()); // so we get a new object, so descendants rerender
166
170
 
167
171
  if (currentSelectionLength) {
168
172
  const { items, max, min, } = getMaxMinSelectionIndices();
@@ -189,10 +193,10 @@ export default function withSelection(WrappedComponent) {
189
193
  },
190
194
  isInSelection = (item) => {
191
195
  if (Repository) {
192
- return inArray(item, localSelection.current);
196
+ return inArray(item, getSelection());
193
197
  }
194
198
 
195
- const found = _.find(localSelection.current, (selectedItem) => {
199
+ const found = _.find(getSelection(), (selectedItem) => {
196
200
  return selectedItem[idIx] === item[idIx];
197
201
  });
198
202
  return !!found;
@@ -214,10 +218,10 @@ export default function withSelection(WrappedComponent) {
214
218
  return found;
215
219
  },
216
220
  getIdsFromLocalSelection = () => {
217
- if (!localSelection.current[0]) {
221
+ if (!getSelection()[0]) {
218
222
  return null;
219
223
  }
220
- const values = _.map(localSelection.current, (item) => {
224
+ const values = _.map(getSelection(), (item) => {
221
225
  if (Repository) {
222
226
  return item.id;
223
227
  }
@@ -301,7 +305,7 @@ export default function withSelection(WrappedComponent) {
301
305
  }
302
306
  }
303
307
 
304
- if (!_.isEqual(newSelection, localSelection.current)) {
308
+ if (!_.isEqual(newSelection, getSelection())) {
305
309
  setSelection(newSelection);
306
310
  }
307
311
  };
@@ -352,7 +356,7 @@ export default function withSelection(WrappedComponent) {
352
356
  }, [value]);
353
357
 
354
358
  if (self) {
355
- self.selection = localSelection.current;
359
+ self.selection = getSelection();
356
360
  self.setSelection = setSelection;
357
361
  self.selectPrev = selectPrev;
358
362
  self.selectNext = selectNext;
@@ -395,7 +399,8 @@ export default function withSelection(WrappedComponent) {
395
399
  {...props}
396
400
  ref={ref}
397
401
  disableWithSelection={false}
398
- selection={localSelection.current}
402
+ selection={getSelection()}
403
+ getSelection={getSelection}
399
404
  setSelection={setSelection}
400
405
  selectionMode={selectionMode}
401
406
  selectPrev={selectPrev}
@@ -61,7 +61,7 @@ export function clickXButton(parentSelectors) {
61
61
  }
62
62
  export function clickXButtonIfEnabled(parentSelectors) {
63
63
  cy.log('clickXButtonIfEnabled');
64
- return clickButtonIfEnabled(parentSelectors, 'xBtn', true);
64
+ return clickButtonIfEnabled(parentSelectors, 'xBtn');
65
65
  }
66
66
  export function clickTrigger(parentSelectors) {
67
67
  cy.log('clickTrigger');
@@ -91,29 +91,29 @@ export function toSideMode(parentSelectors) {
91
91
  cy.log('toSideMode');
92
92
  return clickButtonIfEnabled(parentSelectors, 'sideModeBtn');
93
93
  }
94
- export function clickButton(parentSelectors, name) { // requires the button to be enabled
94
+ export function clickButton(parentSelectors, name, options) { // requires the button to be enabled
95
95
  if (_.isString(parentSelectors)) {
96
96
  parentSelectors = [parentSelectors];
97
97
  }
98
98
  cy.log('clickButton ' + name);
99
- return getDomNode([...parentSelectors, name])
99
+ return getDomNode([...parentSelectors, name], options)
100
100
  .should('not.have.attr', 'data-disabled', 'true') // Check that the element is not disabled
101
101
  .click({ force: true });
102
102
  }
103
- export function clickButtonIfEnabled(parentSelectors, name) { // allows button to be disabled
103
+ export function clickButtonIfEnabled(parentSelectors, name, options) { // allows button to be disabled
104
104
  if (_.isString(parentSelectors)) {
105
105
  parentSelectors = [parentSelectors];
106
106
  }
107
- return getDomNode([...parentSelectors, name])
107
+ return getDomNode([...parentSelectors, name], options)
108
108
  .click({ force: true });
109
109
  }
110
- export function clickButtonIfExists(parentSelectors, name) {
110
+ export function clickButtonIfExists(parentSelectors, name, options) {
111
111
  if (_.isString(parentSelectors)) {
112
112
  parentSelectors = [parentSelectors];
113
113
  }
114
- return getDomNode([...parentSelectors, name]).if().then((node) => { // NOTE if() is a cypress-if function
114
+ return getDomNode([...parentSelectors, name], options).if().then((node) => { // NOTE if() is a cypress-if function
115
115
  if (node) {
116
- node.click();
116
+ cy.get(node).click();
117
117
  }
118
118
  });
119
119
  }
@@ -2,6 +2,7 @@ import {
2
2
  fixInflector,
3
3
  getLastPartOfPath,
4
4
  bootstrapRouteWaiters,
5
+ stubWindowOpen,
5
6
  } from './utilities.js';
6
7
  import {
7
8
  login,
@@ -11,6 +12,7 @@ import {
11
12
  import {
12
13
  getDomNode,
13
14
  getDomNodes,
15
+ ifExists,
14
16
  } from './dom_functions.js';
15
17
  import {
16
18
  hasRowWithFieldValue,
@@ -116,6 +118,39 @@ export function crudJson(selector, newData, editData, schema, ancillaryData, lev
116
118
  // do nothing for now
117
119
  }
118
120
 
121
+ // Editor
122
+ export function viewPdf(editorSelector, formSelector) {
123
+ ifExists(formSelector, 'viewPdfBtn', (btn) => {
124
+ clickButton(formSelector, 'viewPdfBtn');
125
+
126
+ cy.wait(1000); // allow time for modal to render
127
+ const modalSelector = editorSelector + '/chooseFieldsForm';
128
+ clickButton(modalSelector, 'submitBtn');
129
+ cy.wait(1000); // allow time for pdf to download
130
+ cy.get('@windowOpen').should('be.calledWithMatch', /viewModelPdf/);
131
+ });
132
+ }
133
+ export function emailPdf(editorSelector, formSelector) {
134
+ ifExists(formSelector, 'emailPdfBtn', (btn) => {
135
+ clickButton(formSelector, 'emailPdfBtn');
136
+
137
+ cy.wait(1000); // allow time for modal to render
138
+ const modalSelector = editorSelector + '/chooseFieldsForm';
139
+ clickButton(modalSelector, 'submitBtn');
140
+ cy.wait(1000); // allow time for new modal to render
141
+
142
+ // UI now shows the email modal
143
+ fillForm(modalSelector, { email: 'scott@onehat.com', message: 'Sample message', }, { email: 'Input', message: 'TextArea', });
144
+ clickButton(modalSelector, 'submitBtn');
145
+ cy.wait('@emailModelPdf');
146
+
147
+ getDomNode('InfoModal')
148
+ .should('exist')
149
+ .should('contain', 'Email sent successfully.');
150
+ clickButton('InfoModal', 'okBtn');
151
+ });
152
+ }
153
+
119
154
 
120
155
  // Grid
121
156
  export function crudWindowedGridRecord(gridSelector, newData, editData, schema, ancillaryData, level = 0) {
@@ -313,7 +348,11 @@ export function editGridRecord(gridSelector, fieldValues, schema, id, level = 0,
313
348
 
314
349
  verifyNoErrorBox();
315
350
  // cy.wait(1000);
316
-
351
+
352
+ if (whichEditor !== INLINE) {
353
+ viewPdf(editorSelector, formSelector);
354
+ emailPdf(editorSelector, formSelector);
355
+ }
317
356
  }
318
357
  export function editWindowedGridRecord(gridSelector, fieldValues, schema, id, level = 0) {
319
358
  // edits the record as normal, then closes the editor window
@@ -542,6 +581,11 @@ export function editTreeRecord(treeSelector, fieldValues, schema, id, level = 0,
542
581
 
543
582
  verifyNoErrorBox();
544
583
  // cy.wait(1000);
584
+
585
+ if (whichEditor !== INLINE) {
586
+ viewPdf(editorSelector, formSelector);
587
+ emailPdf(editorSelector, formSelector);
588
+ }
545
589
 
546
590
  }
547
591
  export function editWindowedTreeRecord(treeSelector, fieldValues, schema, id, level = 0) {
@@ -591,6 +635,7 @@ export function runClosureTreeControlledManagerScreenCrudTests(model, schema, ne
591
635
  navigateViaTabOrHomeButtonTo(url);
592
636
  }
593
637
  });
638
+ stubWindowOpen();
594
639
  });
595
640
 
596
641
  afterEach(function () {
@@ -688,6 +733,7 @@ export function runClosureTreeManagerScreenCrudTests(args) {
688
733
  navigateViaTabOrHomeButtonTo(url);
689
734
  }
690
735
  });
736
+ stubWindowOpen();
691
737
  });
692
738
 
693
739
  // afterEach(function () {
@@ -753,6 +799,7 @@ export function runManagerScreenCrudTests(args) {
753
799
  navigateViaTabOrHomeButtonTo(url);
754
800
  }
755
801
  });
802
+ stubWindowOpen();
756
803
  });
757
804
 
758
805
  // afterEach(function () {
@@ -806,11 +853,13 @@ export function runReportsManagerTests(reportData) {
806
853
  beforeEach(function () {
807
854
  bootstrapRouteWaiters();
808
855
  login();
856
+ cy.restoreLocalStorage();
809
857
  cy.url().then((currentUrl) => {
810
858
  if (!currentUrl.endsWith(url)) {
811
859
  navigateViaTabOrHomeButtonTo(url);
812
860
  }
813
861
  });
862
+ stubWindowOpen();
814
863
  });
815
864
 
816
865
  _.each(reportData, (report) => {
@@ -828,14 +877,14 @@ export function runReportsManagerTests(reportData) {
828
877
 
829
878
  // Press Excel button
830
879
  clickButton(selector, 'excelBtn');
831
- cy.wait('@getWaiter', { timeout: 10000 }).then((interception) => {
880
+ cy.wait('@getReportWaiter', { timeout: 10000 }).then((interception) => {
832
881
  expect(interception.response.headers['content-type']).to.include('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
833
882
  });
834
883
 
835
884
 
836
885
  // Press PDF button
837
886
  clickButton(selector, 'pdfBtn');
838
- cy.wait('@getReportWaiter', { timeout: 10000 }).then((interception) => {
887
+ cy.wait('@postReportWaiter', { timeout: 10000 }).then((interception) => {
839
888
  expect(interception.response.headers['content-type']).to.include('pdf');
840
889
  });
841
890
 
@@ -8,7 +8,14 @@
8
8
  * @return Cypress chainer
9
9
  */
10
10
  export function getDomNode(selectors, options = {}) {
11
- return cy.get(getTestIdSelectors(selectors, true), options);
11
+ let useFirst;
12
+ if (options?.hasOwnProperty('first')) {
13
+ useFirst = options.first;
14
+ delete options.first;
15
+ } else {
16
+ useFirst = true;
17
+ }
18
+ return cy.get(getTestIdSelectors(selectors, useFirst), options);
12
19
  }
13
20
 
14
21
  /**
@@ -22,6 +29,31 @@ export function getDomNodes(selectors, options = {}) {
22
29
  return cy.get(getTestIdSelectors(selectors), options);
23
30
  }
24
31
 
32
+ /**
33
+ * Verifies that an element with the given selectors does not exist in the DOM.
34
+ * @argument {string | string[]} selectors - data-testid attribute values
35
+ * If an array is given, these will be considered nested selectors.
36
+ * e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
37
+ */
38
+ export function verifyElementDoesNotExist(selectors) {
39
+ cy.get('body').then(($body) => {
40
+ const selectorString = getTestIdSelectors(selectors);
41
+ if ($body.find(selectorString).length > 0) {
42
+ throw new Error(`Element with selectors ${selectorString} exists in the DOM, when it should not`);
43
+ }
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Verifies that an element with the given selectors exists in the DOM.
49
+ * @argument {string | string[]} selectors - data-testid attribute values
50
+ * If an array is given, these will be considered nested selectors.
51
+ * e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
52
+ */
53
+ export function verifyElementExists(selectors) {
54
+ cy.get(getTestIdSelectors(selectors)).should('exist');
55
+ }
56
+
25
57
  /**
26
58
  * Builds selector string for data-testid attributes.
27
59
  * It leaves classname, id, and attribute selectors unchanged.
@@ -79,4 +111,23 @@ export function drag(draggableSelectors, droppableSelectors, options = {}) {
79
111
  .then((success) => {
80
112
  debugger;
81
113
  });
82
- }
114
+ }
115
+
116
+
117
+ // conditions functions that make use of cypress-if
118
+ export function ifExists(parentSelectors, name, cb) {
119
+ if (_.isString(parentSelectors)) {
120
+ parentSelectors = [parentSelectors];
121
+ }
122
+ return getDomNode([...parentSelectors, name]).if().then((node) => {
123
+ if (node) {
124
+ cb(node);
125
+ }
126
+ });
127
+ }
128
+ export function ifNotExists(parentSelectors, name, cb) {
129
+ if (_.isString(parentSelectors)) {
130
+ parentSelectors = [parentSelectors];
131
+ }
132
+ return getDomNode([...parentSelectors, name]).if('not.exist').then(cb);
133
+ }