@globisnv/cypress-core 0.0.10 → 1.1.0

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.
Files changed (52) hide show
  1. package/biome.json +11 -0
  2. package/dist/blueprints/GLO_consignment.js +1 -0
  3. package/dist/blueprints/GLO_consignmentCustoms.d.ts +24 -0
  4. package/dist/blueprints/GLO_consignmentCustoms.js +24 -0
  5. package/dist/blueprints/GLO_consignmentCustomsSettings.d.ts +11 -0
  6. package/dist/blueprints/GLO_consignmentCustomsSettings.js +11 -0
  7. package/dist/blueprints/GLO_customsDeclaration.d.ts +22 -0
  8. package/dist/blueprints/GLO_customsDeclaration.js +22 -0
  9. package/dist/blueprints/GLO_customsDeclarationDetail.d.ts +20 -0
  10. package/dist/blueprints/GLO_customsDeclarationDetail.js +20 -0
  11. package/dist/blueprints/GLO_customsDeclarationDocument.d.ts +9 -0
  12. package/dist/blueprints/GLO_customsDeclarationDocument.js +9 -0
  13. package/dist/blueprints/GLO_dangerousGoods.d.ts +10 -0
  14. package/dist/blueprints/GLO_dangerousGoods.js +9 -0
  15. package/dist/blueprints/GLO_logisticContract.d.ts +14 -0
  16. package/dist/blueprints/GLO_logisticContract.js +53 -11
  17. package/dist/blueprints/GLO_logisticContractCustomsDefinition.d.ts +22 -0
  18. package/dist/blueprints/GLO_logisticContractCustomsDefinition.js +22 -0
  19. package/dist/blueprints/GLO_logisticContractDetail.d.ts +11 -1
  20. package/dist/blueprints/GLO_logisticContractDetail.js +33 -8
  21. package/dist/blueprints/GLO_logisticContractDetailCustoms.d.ts +11 -0
  22. package/dist/blueprints/GLO_logisticContractDetailCustoms.js +11 -0
  23. package/dist/blueprints/GLO_logisticContractDetailHandling.d.ts +1 -0
  24. package/dist/blueprints/GLO_logisticContractDetailHandling.js +1 -0
  25. package/dist/blueprints/GLO_logisticContractDetailItem.js +1 -1
  26. package/dist/blueprints/GLO_logisticContractItem.d.ts +9 -0
  27. package/dist/blueprints/GLO_logisticContractItem.js +31 -4
  28. package/dist/blueprints/GLO_logisticLocationType.d.ts +4 -0
  29. package/dist/blueprints/GLO_logisticLocationType.js +4 -0
  30. package/dist/blueprints/GLO_planBloc.d.ts +6 -0
  31. package/dist/blueprints/GLO_planBloc.js +6 -0
  32. package/dist/blueprints/GLO_priceEntry.d.ts +1 -0
  33. package/dist/blueprints/GLO_priceEntry.js +1 -0
  34. package/dist/blueprints/GLO_purchaseOrderLine.js +4 -0
  35. package/dist/blueprints/GLO_relations.js +4 -4
  36. package/dist/blueprints/GLO_stopSchedule.d.ts +11 -0
  37. package/dist/blueprints/GLO_stopSchedule.js +11 -0
  38. package/dist/blueprints/GLO_transportScenario.d.ts +4 -0
  39. package/dist/blueprints/GLO_transportScenario.js +4 -0
  40. package/dist/blueprints/GLO_transportScenarioExecution.d.ts +6 -0
  41. package/dist/blueprints/GLO_transportScenarioExecution.js +5 -0
  42. package/dist/blueprints/GLO_transportScenarioRegion.d.ts +5 -0
  43. package/dist/blueprints/GLO_transportScenarioRegion.js +5 -0
  44. package/dist/blueprints/GLO_zoneTypeEntityType.d.ts +4 -0
  45. package/dist/blueprints/GLO_zoneTypeEntityType.js +4 -0
  46. package/dist/commands/GLO_commands.js +72 -39
  47. package/dist/commands/GLT_commands.js +2 -2
  48. package/dist/config.js +4 -2
  49. package/dist/websocketDetector.js +148 -114
  50. package/package.json +9 -6
  51. package/template/.gitattributes +12 -0
  52. package/template/package.json +5 -2
@@ -1,4 +1,4 @@
1
- import { open } from '../tsHelper.js';
1
+ import { close, open } from '../tsHelper.js';
2
2
  export const GLO_enterLogisticContractItem = (obj) => {
3
3
  const formName = 'ng$logistic_contract_item_dtl';
4
4
  const generalFormName = 'ng$logistic_contract_item_dtl_general';
@@ -33,8 +33,35 @@ export const GLO_enterLogisticContractItem = (obj) => {
33
33
  cy.GLO_checkLoadingIndicator();
34
34
  }
35
35
  }
36
- if (obj.flagProformaInvoice) {
37
- cy.GLO_selectTab([formName, 'tabs_70'], 'Billing', open);
38
- cy.GLO_checkbox(['ng$logistic_contract_item_dtl_invoicing', 'flag_proforma_invoice'], obj.flagProformaInvoice);
36
+ // Tab Billing
37
+ enterBillingTab(obj);
38
+ };
39
+ // Billing tab
40
+ const enterBillingTab = (obj) => {
41
+ const formName = 'ng$logistic_contract_item_dtl';
42
+ const invoicingFormName = 'ng$logistic_contract_item_dtl_invoicing';
43
+ if (obj.flagProformaInvoice === undefined &&
44
+ obj.flagDirectInvoice === undefined &&
45
+ obj.flagSummaryInvoice === undefined &&
46
+ obj.flagAutomatedInvoice === undefined &&
47
+ obj.automatedInvoiceDays === undefined &&
48
+ !obj.invoicePeriodicity &&
49
+ !obj.invoicePeriodicityQuantity &&
50
+ !obj.dayOfMonthOfInvoice &&
51
+ !obj.transactionDateType &&
52
+ !obj.invoiceGroup) {
53
+ return;
39
54
  }
55
+ cy.GLO_selectTab([formName, 'tabs_70'], 'Billing', open);
56
+ cy.GLO_checkbox([invoicingFormName, 'flag_proforma_invoice'], obj.flagProformaInvoice);
57
+ cy.GLO_checkbox([invoicingFormName, 'flag_summary_invoice'], obj.flagSummaryInvoice);
58
+ cy.GLO_checkbox([invoicingFormName, 'flag_direct_invoice'], obj.flagDirectInvoice);
59
+ cy.GLO_checkbox([invoicingFormName, 'flag_automated_invoice'], obj.flagAutomatedInvoice);
60
+ cy.GLO_addInput([invoicingFormName, 'automated_invoice_days'], obj.automatedInvoiceDays);
61
+ cy.GLO_addInput([invoicingFormName, 'invoice_periodicity'], obj.invoicePeriodicity);
62
+ cy.GLO_addInput([invoicingFormName, 'invoice_periodicity_quantity'], obj.invoicePeriodicityQuantity);
63
+ cy.GLO_addInput([invoicingFormName, 'day_of_month_of_invoice'], obj.dayOfMonthOfInvoice);
64
+ cy.GLO_addInput([invoicingFormName, 'transaction_date_type'], obj.transactionDateType);
65
+ cy.GLO_addInput([invoicingFormName, 'invoice_group'], obj.invoiceGroup);
66
+ cy.GLO_selectTab([formName, 'tabs_70'], 'Billing', close);
40
67
  };
@@ -0,0 +1,4 @@
1
+ export type objLogisticLocationType = {
2
+ logisticLocationTypeCode?: string;
3
+ };
4
+ export declare const GLO_enterLogisticLocationType: (obj: objLogisticLocationType) => void;
@@ -0,0 +1,4 @@
1
+ export const GLO_enterLogisticLocationType = (obj) => {
2
+ const formName = 'ng$logistic_location_type_dtl';
3
+ cy.GLO_addInput([formName, 'logistic_location_type_code'], obj.logisticLocationTypeCode);
4
+ };
@@ -0,0 +1,6 @@
1
+ export type objPlanBloc = {
2
+ planBlocCode?: string;
3
+ planBlocName?: string;
4
+ company?: string;
5
+ };
6
+ export declare const GLO_enterPlanBloc: (obj: objPlanBloc) => void;
@@ -0,0 +1,6 @@
1
+ export const GLO_enterPlanBloc = (obj) => {
2
+ const formName = 'ng$plan_bloc_dtl';
3
+ cy.GLO_addInput([formName, 'plan_bloc_code'], obj.planBlocCode);
4
+ cy.GLO_addInput([formName, 'plan_bloc_name'], obj.planBlocName);
5
+ cy.GLO_addInput([formName, 'sec_company_id'], obj.company);
6
+ };
@@ -4,6 +4,7 @@ export type objPriceEntry = {
4
4
  validTill?: string;
5
5
  currency?: string;
6
6
  result?: number;
7
+ resultPer?: number;
7
8
  perType?: string;
8
9
  priceAmount?: number;
9
10
  pricePerUnit?: string;
@@ -5,6 +5,7 @@ export const GLO_enterPriceEntry = (obj) => {
5
5
  cy.GLO_addDate(input, [formName, 'valid_till'], obj.validTill);
6
6
  cy.GLO_addInput([formName, 'currency_id'], obj.currency);
7
7
  cy.GLO_addInput([formName, 'result_value'], obj.result);
8
+ cy.GLO_addInput([formName, 'result_value_per'], obj.resultPer);
8
9
  cy.GLO_addInput(['result_value_per_unit_id'], obj.perType);
9
10
  cy.GLO_addInput([formName, 'min_amount'], obj.minAmount);
10
11
  cy.GLO_addInput([formName, 'max_amount'], obj.maxAmount);
@@ -9,7 +9,11 @@ export const GLO_enterPurchaseOrderLine = (obj) => {
9
9
  if (obj.quantity || obj.generalPrice || obj.inputQtyPerUnitId) {
10
10
  cy.GLO_selectTab([formName, detailTabsName], 'General', open);
11
11
  cy.GLO_addInput([`${formName}_general`, 'input_qty_per_unit_id'], obj.inputQtyPerUnitId);
12
+ if (obj.inputQtyPerUnitId)
13
+ cy.GLO_checkLoadingIndicator();
12
14
  cy.GLO_addInput([`${formName}_general`, 'input_qty'], obj.quantity);
15
+ if (obj.quantity)
16
+ cy.GLO_checkLoadingIndicator();
13
17
  cy.GLO_addInput([`${formName}_general`, 'quantity'], obj.quantityBase);
14
18
  cy.GLO_checkField([`${formName}_general`, 'quantity'], obj.quantityBase);
15
19
  cy.GLO_addInput([`${formName}_general`, 'price'], obj.generalPrice);
@@ -14,8 +14,8 @@ export const GLO_enterRelation = (obj, formName = formNameString) => {
14
14
  cy.GLO_addInput([formName, 'postal_code_general'], obj.postalCodeGeneral);
15
15
  cy.GLO_checkLoadingIndicator();
16
16
  cy.get('body').then(($body) => {
17
- if ($body.find('.svy-dialog').length > 0) {
18
- if ($body.find('.svy-dialog [row-index]').length === 0) {
17
+ if ($body.find('.svy-dialog.active').length > 0) {
18
+ if ($body.find('.svy-dialog.active [row-index]').length === 0) {
19
19
  cy.GLO_log('No cities found for this postal code, allowing manual input');
20
20
  cy.GLO_clickButton(['ng$svy_nav_selection', 'btn_cancel']);
21
21
  return;
@@ -43,8 +43,8 @@ export const GLO_enterRelation = (obj, formName = formNameString) => {
43
43
  cy.GLO_addInput([formName, 'city_general'], obj.cityGeneral);
44
44
  cy.GLO_checkLoadingIndicator();
45
45
  cy.get('body').then(($body) => {
46
- if ($body.find('.svy-dialog').length > 0) {
47
- if ($body.find('.svy-dialog [row-index]').length === 0)
46
+ if ($body.find('.svy-dialog.active').length > 0) {
47
+ if ($body.find('.svy-dialog.active [row-index]').length === 0)
48
48
  cy.GLO_clickButton(['ng$svy_nav_selection', 'btn_cancel']);
49
49
  else
50
50
  cy.GLO_dblClickRowInGrid(span, ['ng$postalcode_sel', 'grid'], obj.cityGeneral, undefined, skipCheck);
@@ -1,4 +1,15 @@
1
+ export type objStopScheduleDay = {
2
+ /** 1 = Monday, 7 = Sunday. */
3
+ day: number;
4
+ amFrom?: string;
5
+ amTill?: string;
6
+ pmFrom?: string;
7
+ pmTill?: string;
8
+ };
1
9
  export type objStopSchedule = {
2
10
  code?: string;
11
+ every?: number;
12
+ unit?: string;
13
+ days?: objStopScheduleDay[];
3
14
  };
4
15
  export declare const GLO_enterStopSchedule: (obj: objStopSchedule) => void;
@@ -1,4 +1,15 @@
1
+ import { check } from '../tsHelper.js';
1
2
  export const GLO_enterStopSchedule = (obj) => {
2
3
  const formName = 'ng$delivery_schedule$logistic_contract_detail_stop_dtl';
3
4
  cy.GLO_addInput([formName, 'schedule_code'], obj.code);
5
+ cy.GLO_addInput([formName, 'every'], obj.every);
6
+ cy.GLO_addInput([formName, 'unit'], obj.unit);
7
+ // The times are named per weekday, the checkbox drops the flag prefix
8
+ obj.days?.forEach((day) => {
9
+ cy.GLO_checkbox([formName, `day${day.day}`], check);
10
+ cy.GLO_addInput([formName, `amfrom${day.day}`], day.amFrom);
11
+ cy.GLO_addInput([formName, `amto${day.day}`], day.amTill);
12
+ cy.GLO_addInput([formName, `pmfrom${day.day}`], day.pmFrom);
13
+ cy.GLO_addInput([formName, `pmto${day.day}`], day.pmTill);
14
+ });
4
15
  };
@@ -0,0 +1,4 @@
1
+ export type objTransportScenario = {
2
+ transportScenarioCode?: string;
3
+ };
4
+ export declare const GLO_enterTransportScenario: (obj: objTransportScenario) => void;
@@ -0,0 +1,4 @@
1
+ export const GLO_enterTransportScenario = (obj) => {
2
+ const formName = 'ng$tpt_scenario_dtl';
3
+ cy.GLO_addInput([formName, 'tpt_scenario_code'], obj.transportScenarioCode);
4
+ };
@@ -0,0 +1,6 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objTransportScenarioExecution = {
3
+ isActive?: CheckState;
4
+ description?: string;
5
+ };
6
+ export declare const GLO_enterTransportScenarioExecution: (obj: objTransportScenarioExecution) => void;
@@ -0,0 +1,5 @@
1
+ export const GLO_enterTransportScenarioExecution = (obj) => {
2
+ const formName = 'ng$tpt_scenario_execution_dtl';
3
+ cy.GLO_checkbox([formName, 'is_active'], obj.isActive);
4
+ cy.GLO_addInput([formName, 'description'], obj.description);
5
+ };
@@ -0,0 +1,5 @@
1
+ export type objTransportScenarioRegion = {
2
+ fromCountry?: string;
3
+ toCountry?: string;
4
+ };
5
+ export declare const GLO_enterTransportScenarioRegion: (obj: objTransportScenarioRegion) => void;
@@ -0,0 +1,5 @@
1
+ export const GLO_enterTransportScenarioRegion = (obj) => {
2
+ const formName = 'ng$tpt_scenario_region_region_dtl';
3
+ cy.GLO_addInput([formName, 'from_country_id'], obj.fromCountry);
4
+ cy.GLO_addInput([formName, 'to_country_id'], obj.toCountry);
5
+ };
@@ -0,0 +1,4 @@
1
+ export type objZoneTypeEntityType = {
2
+ entityType?: string;
3
+ };
4
+ export declare const GLO_enterZoneTypeEntityType: (obj: objZoneTypeEntityType) => void;
@@ -0,0 +1,4 @@
1
+ export const GLO_enterZoneTypeEntityType = (obj) => {
2
+ const formName = 'ng$zone_type_entity_type_dtl';
3
+ cy.GLO_addInput([formName, 'entity_type'], obj.entityType);
4
+ };
@@ -33,13 +33,13 @@ const isGridRowSelector = (baseSelector, index) => {
33
33
  // rendered-but-scrolled-past rows as hidden (measured: 14 of 21 rendered rows drop out of `:visible`
34
34
  // after scrolling a grid to the bottom). Filter on the container, which is what both were after.
35
35
  const isInOpenContainer = (_, el) => !el.closest('[hidden]');
36
- const pickByRowIndex = (chain, index, log = false) => {
36
+ const pickByRowIndex = (chain, index, log = false, timeout = defaultTimeout) => {
37
37
  if (log)
38
38
  Cypress.log({ name: 'idx', message: String(index) });
39
39
  return chain.filter((i, el) => {
40
40
  const rowIndexEl = el.closest('[row-index]');
41
41
  return rowIndexEl ? rowIndexEl.getAttribute('row-index') === String(index) : i === index;
42
- });
42
+ }, { timeout });
43
43
  };
44
44
  Cypress.Commands.add('GLO_getElement', (selectorArg, options = {}) => {
45
45
  const arr = selectorArg;
@@ -60,12 +60,12 @@ Cypress.Commands.add('GLO_getElement', (selectorArg, options = {}) => {
60
60
  const elementInCrumb = Cypress.$(`div[formslist] ${selector}`).length > 0;
61
61
  if (elementInCrumb) {
62
62
  const result = cy.get(`div[formslist]:not([hidden]) ${selector}`, getOpts);
63
- return pickByRowIndex(result, index ?? 0, hasIndex);
63
+ return pickByRowIndex(result, index ?? 0, hasIndex, getOpts.timeout);
64
64
  }
65
65
  }
66
66
  const result = cy.get(selector, getOpts);
67
67
  // Ponytail: sync DOM check — if element absent, skip filter so cy.get + should('not.exist') chain works
68
- return hasIndex || Cypress.$(selector).length > 0 ? pickByRowIndex(result, index ?? 0, hasIndex) : result;
68
+ return hasIndex || Cypress.$(selector).length > 0 ? pickByRowIndex(result, index ?? 0, hasIndex, getOpts.timeout) : result;
69
69
  });
70
70
  Cypress.Commands.add('GLO_getVisible', (elementName, timeOut = defaultTimeout) => {
71
71
  return cy.get(elementName, { timeout: timeOut }).filter(':visible', { timeout: timeOut });
@@ -115,7 +115,7 @@ Cypress.Commands.add('GLO_defaultlogin', (company, usernameENV = 'username', pas
115
115
  }
116
116
  cy.GLO_checkLoadingIndicator();
117
117
  cy.get('body').then(($body) => {
118
- if ($body.find('.svy-message-dialog .window-title').text() === 'Locked record') {
118
+ if ($body.find('.svy-message-dialog.active .window-title').text() === 'Locked record') {
119
119
  // biome-ignore plugin: wait for locked record dialog choice to register
120
120
  cy.wait(15000);
121
121
  cy.GLO_dialogChoice('!globis.label.yes!');
@@ -136,7 +136,7 @@ const fillInUserAndPasswordDefault = (usernameENV, passwordENV) => {
136
136
  .type(Cypress.expose(usernameENV));
137
137
  cy.env([passwordENV]).then((envVars) => {
138
138
  cy.GLO_getElement(['ng2$svy_nav_login', 'txtPass'])
139
- .find('input')
139
+ .find('input:visible', { timeout: 30000 })
140
140
  .eq(0)
141
141
  .clear()
142
142
  .type(envVars[passwordENV], { log: false });
@@ -227,10 +227,21 @@ Cypress.Commands.add('GLO_oktaLogin', (company, urlType, skipvNavCheck) => {
227
227
  cy.task('log', 'End navbar check');
228
228
  });
229
229
  Cypress.Commands.add('GLO_logout', () => {
230
- cy.GLO_getElement(['ng$main_form2', 'navbar_top']).find('.lucide-LogOut').click();
231
- cy.GLO_getVisible('.window-footer button.svy-btn-primary', 30000)
232
- .contains(/^(Yes|Ja)$/)
233
- .click();
230
+ cy.document({ log: false }).then((doc) => {
231
+ cy.GLO_getElement(['ng$main_form2', 'navbar_top']).find('.lucide-LogOut').click();
232
+ cy.GLO_getVisible('.window-footer button.svy-btn-primary', 30000)
233
+ .contains(/^(Yes|Ja)$/)
234
+ .click();
235
+ // The confirm click triggers the app's own client-side redirect (not a cy.visit); a full
236
+ // page reload swaps the Document object as soon as navigation starts, so also wait for
237
+ // readyState to reach 'complete' — otherwise this resolves the instant the reload begins,
238
+ // not once it's actually finished. Checking readyState (not the destination URL) survives
239
+ // moving to a stateless auth flow with no login screen.
240
+ cy.document({ log: false, timeout: 30000 }).should((newDoc) => {
241
+ expect(newDoc).not.to.equal(doc);
242
+ expect(newDoc.readyState).to.equal('complete');
243
+ });
244
+ });
234
245
  });
235
246
  Cypress.Commands.add('GLO_servoyLogin', (company) => {
236
247
  if (cypressWebsocketLoader) {
@@ -241,13 +252,13 @@ Cypress.Commands.add('GLO_servoyLogin', (company) => {
241
252
  }
242
253
  cy.GLO_checkLoadingIndicator();
243
254
  cy.GLO_getElement(['ng2$svy_nav_login', 'txtUser'], { timeout: 30000 })
244
- .find('input')
255
+ .find('input:visible', { timeout: 30000 })
245
256
  .eq(0)
246
257
  .clear()
247
258
  .type(Cypress.expose('username'));
248
259
  cy.env(['password']).then(({ password }) => {
249
260
  cy.GLO_getElement(['ng2$svy_nav_login', 'txtPass'])
250
- .find('input')
261
+ .find('input:visible', { timeout: 30000 })
251
262
  .eq(0)
252
263
  .clear()
253
264
  .type(password, { log: false });
@@ -272,13 +283,13 @@ Cypress.Commands.add('GLO_clearData', (formulaName) => {
272
283
  cy.GLO_log('Delete records completed');
273
284
  });
274
285
  Cypress.Commands.add('GLO_clickButton', (selector, buttonType) => {
275
- cy.GLO_getElement(selector)
276
- .then(($element) => {
277
- buttonType ? cy.wrap($element).find(`.${buttonType}`).click() : cy.wrap($element).click();
278
- })
279
- .then(() => {
280
- cy.GLO_checkLoadingIndicator();
281
- });
286
+ // Chain .click() directly onto the query (no .then()/cy.wrap() snapshot) so Cypress's
287
+ // retry-ability re-runs GLO_getElement fresh on every attempt. A snapshotted jQuery handle
288
+ // goes stale and never recovers if Angular destroys/recreates the node once real data loads
289
+ // (e.g. a dialog opened via keyboard shortcut, before its websocket round-trip completes).
290
+ const target = buttonType ? cy.GLO_getElement(selector).find(`.${buttonType}`) : cy.GLO_getElement(selector);
291
+ target.click({ timeout: 10000 });
292
+ cy.GLO_checkLoadingIndicator();
282
293
  });
283
294
  Cypress.Commands.add('GLO_clickButtonText', (textButton) => {
284
295
  clickButtonTextHelper(textButton, '.dx-button-content');
@@ -682,12 +693,12 @@ Cypress.Commands.add('GLO_closeDashboard', () => {
682
693
  if (testFailed)
683
694
  return;
684
695
  cy.get('.navigation-item-close', { timeout: 10000 }).click();
685
- cy.GLO_getVisible('.svy-message-dialog', 30000).should('exist');
686
- cy.get('.svy-message-dialog').should(($dialog) => {
696
+ cy.GLO_getVisible('.svy-message-dialog.active', 30000).should('exist');
697
+ cy.get('.svy-message-dialog.active').should(($dialog) => {
687
698
  expect($dialog.find('.window-title').text().trim()).to.match(/^(Confirm|Bevestigen)$/);
688
699
  expect($dialog.find('.window-body').text().trim()).to.match(/^(Close all programs\?|Sluit alle programma's\?)$/);
689
700
  });
690
- cy.get('.svy-message-dialog')
701
+ cy.get('.svy-message-dialog.active')
691
702
  .find('.svy-btn-primary')
692
703
  .contains(/^(Yes|Ja)$/)
693
704
  .should('exist')
@@ -763,22 +774,23 @@ Cypress.Commands.add('GLO_dialogChoice', (confirmation) => {
763
774
  cy.wait(500);
764
775
  cy.get('body').then(($body) => {
765
776
  let CSSSearchStr;
766
- if ($body.find('.svy-message-dialog').length > 0) {
767
- CSSSearchStr = '.svy-message-dialog button';
777
+ if ($body.find('.svy-message-dialog.active').length > 0) {
778
+ CSSSearchStr = '.svy-message-dialog.active button';
768
779
  }
769
780
  else if ($body.find('.servoycore-dialog-body').length > 0) {
770
781
  // Previous version
771
782
  CSSSearchStr = '.servoycore-dialog-body button';
772
783
  }
773
- else if ($body.find('.svy-dialog.window').length > 0) {
784
+ else if ($body.find('.svy-dialog.window.active').length > 0) {
774
785
  // Another variant
775
- CSSSearchStr = '.svy-dialog.window globiscomponents-button';
786
+ CSSSearchStr = '.svy-dialog.window.active globiscomponents-button';
776
787
  }
777
788
  else {
778
789
  cy.GLO_log('Popup not found, skipping dialog option');
779
790
  return;
780
791
  }
781
- if ($body.find('.window-title.type-error').length > 0 && $body.find('.window-title.type-error').is(':visible')) {
792
+ const $errorTitle = $body.find('.svy-message-dialog.active .window-title.type-error, .svy-dialog.window.active .window-title.type-error');
793
+ if ($errorTitle.length > 0 && $errorTitle.is(':visible')) {
782
794
  cy.GLO_log('Error dialog, failing test. use GLO_dialogRequiredCheck.');
783
795
  cy.wrap(null).should(() => {
784
796
  throw new Error('Error dialog, failing test. use GLO_dialogRequiredCheck.');
@@ -805,7 +817,7 @@ Cypress.Commands.add('GLO_dialogChoice', (confirmation) => {
805
817
  });
806
818
  });
807
819
  Cypress.Commands.add('GLO_addInputDialog', (text) => {
808
- cy.get('.svy-message-dialog .window-body form', { timeout: 15000 }).then(($form) => {
820
+ cy.get('.svy-message-dialog.active .window-body form', { timeout: 15000 }).then(($form) => {
809
821
  const inputType = $form.children().first().prop('tagName').toLowerCase();
810
822
  if (inputType === 'input') {
811
823
  cy.wrap($form).find('input').clear().type(text);
@@ -989,7 +1001,7 @@ Cypress.Commands.add('GLO_switchCompany', (companyName) => {
989
1001
  cy.GLO_getElement(['ng$main_form2', 'navbar_top']).find('#userroles-dropdown').click();
990
1002
  cy.get('.dx-submenu').find(span).contains('Switch company').click();
991
1003
  cy.GLO_checkLoadingIndicator();
992
- cy.get('.svy-dialog').find('.dx-dropdowneditor-icon').click();
1004
+ cy.get('.svy-dialog.active').find('.dx-dropdowneditor-icon').click();
993
1005
  cy.GLO_getVisible('.dx-scrollview-content').contains(companyName).click();
994
1006
  cy.GLO_clickButton(['ng$login_companies_dlg', 'btn_save']);
995
1007
  });
@@ -1199,12 +1211,12 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1199
1211
  const loadingColumnId = () => columnId
1200
1212
  ? cy.wrap(columnId)
1201
1213
  : cy
1202
- .get('.window-body:visible .ag-center-cols-container [row-index="0"] [role="gridcell"]', { timeout: 30000 })
1214
+ .get('.svy-dialog.active .window-body .ag-center-cols-container [row-index="0"] [role="gridcell"]', { timeout: 30000 })
1203
1215
  .filter((_, el) => Cypress.$(el).text().trim().length > 0)
1204
1216
  .first()
1205
1217
  .invoke('attr', 'col-id');
1206
1218
  // So we dont have to repeat the whole command everywhere, just looks better in code below.
1207
- const searchData = () => loadingColumnId().then((colId) => cy.get(`.window-body [row-index] [col-id="${colId}"]`, { timeout: loadTimeout }).should(($els) => {
1219
+ const searchData = () => loadingColumnId().then((colId) => cy.get(`.svy-dialog.active .window-body [row-index] [col-id="${colId}"]`, { timeout: loadTimeout }).should(($els) => {
1208
1220
  assert.equal($els.toArray().filter((item) => (item.textContent || '').trim() === '').length, 0, 'Grid cells should not have empty values');
1209
1221
  assert.isTrue(Cypress.$('.ag-loading').length === 0 && Cypress.$('loading-indicator').length === 0, '(Grid) loadingindicator is not visible');
1210
1222
  }));
@@ -1238,7 +1250,7 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1238
1250
  searchData()
1239
1251
  .then(($els) => Array.from($els, (el) => el.textContent.trim()))
1240
1252
  .as('beforeSearch');
1241
- cy.GLO_getVisible('.window-body').within(() => {
1253
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1242
1254
  cy.GLO_getVisible(`.ag-header-cell[col-id="${columnId}"]`)
1243
1255
  .invoke('attr', 'aria-colindex')
1244
1256
  .then((ariaColIndex) => {
@@ -1257,13 +1269,13 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1257
1269
  }
1258
1270
  });
1259
1271
  }
1260
- cy.GLO_getVisible('.window-body').within(() => {
1272
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1261
1273
  dblClickRowInGrid(span, value, columnId, equals);
1262
1274
  });
1263
1275
  }
1264
1276
  else {
1265
1277
  cy.log('Value already exists in the grid, skipping search.');
1266
- cy.GLO_getVisible('.window-body').within(() => {
1278
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1267
1279
  dblClickRowInGrid(span, value, columnId, equals);
1268
1280
  });
1269
1281
  }
@@ -1291,7 +1303,7 @@ Cypress.Commands.add('GLO_popupWindow', (selector, value, columnId, equals = noE
1291
1303
  cy.GLO_checkLoadingIndicator();
1292
1304
  }
1293
1305
  cy.get('body').then(($body) => {
1294
- if ($body.find('.window-body').filter(':visible').length > 0) {
1306
+ if ($body.find('.svy-dialog.active .window-body').length > 0) {
1295
1307
  selectFromPopupWindow(value, columnId, equals);
1296
1308
  }
1297
1309
  else {
@@ -1680,7 +1692,12 @@ Cypress.Commands.add('GLO_readFile', (fileName, fileType) => {
1680
1692
  cy.readFile(`${Cypress.config('downloadsFolder')}/${fileName}.${fileType}`);
1681
1693
  });
1682
1694
  Cypress.Commands.add('GLO_dialogRequiredCheck', (confirmation, title, content, contentMatchType = 'exact') => {
1683
- cy.GLO_getVisible('.svy-message-dialog, .svy-dialog', 120000).within(($dialog) => {
1695
+ // Cypress 16's :visible no longer excludes a dialog covered by another one stacked on top
1696
+ // of it (native checkVisibility() doesn't check occlusion), so when two dialogs are open
1697
+ // both can match here. The framework itself already marks the topmost/interactive one with
1698
+ // an "active" class (moved off the covered dialog onto whichever opens on top of it), so
1699
+ // scope to that instead of relying on :visible alone to disambiguate.
1700
+ cy.GLO_getVisible('.svy-message-dialog.active, .svy-dialog.active', 120000).within(($dialog) => {
1684
1701
  if ($dialog.find('.window-title.type-error:visible').length > 0) {
1685
1702
  if (typeof title === 'undefined' && typeof content === 'undefined') {
1686
1703
  cy.GLO_log('Error dialog, failing test. Explicit definition of the title or content required.');
@@ -1770,9 +1787,21 @@ Cypress.Commands.add('GLO_fillInfieldInGrid', (selector, rowFilter, columnId, va
1770
1787
  if (!value)
1771
1788
  return;
1772
1789
  if (typeof rowFilter === 'string') {
1773
- cy.GLO_getAllRowsInGrid().filter(`:contains("${rowFilter}")`).find(`[col-id="${columnId}"]`).click();
1790
+ cy.GLO_getAllRowsInGrid()
1791
+ .filter((_, el) => Cypress.$(el)
1792
+ .find('[role="gridcell"]')
1793
+ .toArray()
1794
+ .some((cell) => cell.textContent?.trim() === rowFilter))
1795
+ .find(`[col-id="${columnId}"]`)
1796
+ .click();
1774
1797
  cy.wait(500); // Else both click and dblclick fire too fast
1775
- cy.GLO_getAllRowsInGrid().filter(`:contains("${rowFilter}")`).find(`[col-id="${columnId}"]`).dblclick();
1798
+ cy.GLO_getAllRowsInGrid()
1799
+ .filter((_, el) => Cypress.$(el)
1800
+ .find('[role="gridcell"]')
1801
+ .toArray()
1802
+ .some((cell) => cell.textContent?.trim() === rowFilter))
1803
+ .find(`[col-id="${columnId}"]`)
1804
+ .dblclick();
1776
1805
  }
1777
1806
  else {
1778
1807
  cy.GLO_getElement(selector).within(() => {
@@ -2177,7 +2206,11 @@ Cypress.Commands.add('GLO_extendedFilter', (filter, clearExistingFilters) => {
2177
2206
  const id = $el[0].getAttribute('aria-controls');
2178
2207
  if (!id)
2179
2208
  throw new Error('Missing aria-controls');
2180
- cy.get(`[id="${id}"]`, { timeout: 30000 }).should('exist').should('not.have.class', 'dx-empty-collection').find('.dx-item').should('have.length.greaterThan', 0);
2209
+ cy.get(`[id="${id}"]`, { timeout: 30000 })
2210
+ .should('be.visible')
2211
+ .should('not.have.class', 'dx-empty-collection')
2212
+ .find('.dx-item')
2213
+ .should('have.length.greaterThan', 0);
2181
2214
  cy.get(`[id="${id}"] .dx-scrollview-content`).children().contains(value).click();
2182
2215
  });
2183
2216
  switch (dropDownType) {
@@ -40,7 +40,7 @@ Cypress.Commands.add('GLT_goHome', (formname) => {
40
40
  cy.GLO_checkLoadingIndicator();
41
41
  });
42
42
  Cypress.Commands.add('GLT_closePopup', () => {
43
- cy.get('.svy-dialog .window-header .svy-dialog-close').click();
43
+ cy.get('.svy-dialog.active .window-header .svy-dialog-close').click();
44
44
  cy.get('.svy-dialog', { timeout: 60000 }).should('not.exist');
45
45
  });
46
46
  Cypress.Commands.add('GLT_clickButtonInButtonBar', (formName, title) => {
@@ -130,7 +130,7 @@ Cypress.Commands.add('GLT_getTreeListRows', (formName, elementName, filters) =>
130
130
  return processLevel(null, 0);
131
131
  });
132
132
  Cypress.Commands.add('GLT_addInputDialog', (text) => {
133
- cy.get('.svy-dialog .window-body, .svy-message-dialog .window-body', { timeout: 15000 })
133
+ cy.get('.svy-dialog.active .window-body, .svy-message-dialog.active .window-body', { timeout: 15000 })
134
134
  .should(($el) => {
135
135
  expect($el.find('input').length + $el.find('select').length, 'input or select present').to.be.greaterThan(0);
136
136
  })
package/dist/config.js CHANGED
@@ -27,7 +27,9 @@ const browserSettings = (browser, launchOptions) => {
27
27
  export function defineGlobisConfig(overrides = {}) {
28
28
  const wsMetricsBuffer = [];
29
29
  return defineConfig({
30
- allowCypressEnv: false,
30
+ // Cypress 16's HTTP/2 support is still an unfinished epic (1/14 done), causing ETIMEDOUT
31
+ // hangs against our Servoy backend. Remove once resolved: https://github.com/cypress-io/cypress/issues/33846
32
+ forceHttp1: true,
31
33
  e2e: {
32
34
  videosFolder: 'cypress/videos',
33
35
  screenshotsFolder: 'cypress/screenshots',
@@ -37,7 +39,7 @@ export function defineGlobisConfig(overrides = {}) {
37
39
  videoCompression: false,
38
40
  responseTimeout: 120000,
39
41
  modifyObstructiveCode: false,
40
- experimentalMemoryManagement: true,
42
+ keystrokeDelay: 10,
41
43
  numTestsKeptInMemory: Number(process.env.numTestsKeptInMemory ?? 0),
42
44
  scrollBehavior: 'center',
43
45
  ...overrides.e2e,