@globisnv/cypress-core 1.0.0 → 1.1.1

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.
@@ -15,6 +15,7 @@ export const GLO_enterConsignment = (obj, formName = defaultFormName, formNameGe
15
15
  cy.GLO_checkLoadingIndicator();
16
16
  }
17
17
  cy.GLO_addInput([formName, 'consignment_subtype'], obj.consignmentSubType);
18
+ cy.GLO_addInput([formName, 'default_consignment_description'], obj.defaultConsignmentDescription);
18
19
  cy.GLO_addInput([formName, 'relation_reference'], obj.relationReference);
19
20
  cy.GLO_addInput([formName, 'delivery_condition'], obj.deliveryCondition);
20
21
  cy.GLO_addInput([formName, 'local_invoice_relation_code'], obj.invoiceRelation);
@@ -0,0 +1,24 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objConsignmentCustoms = {
3
+ sequenceNr?: number;
4
+ customsType?: string;
5
+ customsOfficeDefinition?: string;
6
+ customsOfficeAddress?: string;
7
+ customsResponsibility?: string;
8
+ customsRelation?: string;
9
+ customerCustomsBroker?: string;
10
+ flagBlocked?: CheckState;
11
+ flagWeekendClearance?: CheckState;
12
+ remarks?: string;
13
+ outboundStopDefinition?: string;
14
+ outboundStop?: string;
15
+ outboundHandling?: string;
16
+ outboundDocumentType?: string;
17
+ flagOutboundOk?: CheckState;
18
+ inboundStopDefinition?: string;
19
+ inboundStop?: string;
20
+ inboundHandling?: string;
21
+ inboundDocumentType?: string;
22
+ flagInboundOk?: CheckState;
23
+ };
24
+ export declare const GLO_enterConsignmentCustoms: (obj: objConsignmentCustoms) => void;
@@ -0,0 +1,24 @@
1
+ export const GLO_enterConsignmentCustoms = (obj) => {
2
+ const formName = 'ng$consignment_customs_dtl';
3
+ cy.GLO_addInput([formName, 'sequence_nr'], obj.sequenceNr);
4
+ cy.GLO_addInput([formName, 'customs_type'], obj.customsType);
5
+ cy.GLO_addInput([formName, 'customs_office_definition'], obj.customsOfficeDefinition);
6
+ cy.GLO_popupWindow([formName, 'customs_office_address_id'], obj.customsOfficeAddress);
7
+ cy.GLO_addInput([formName, 'customs_responsibility'], obj.customsResponsibility);
8
+ cy.GLO_popupWindow([formName, 'customs_relation_id'], obj.customsRelation);
9
+ cy.GLO_addInput([formName, 'customer_customs_broker'], obj.customerCustomsBroker);
10
+ cy.GLO_checkbox([formName, 'flag_blocked'], obj.flagBlocked);
11
+ cy.GLO_checkbox([formName, 'flag_weekend_clearance'], obj.flagWeekendClearance);
12
+ cy.GLO_addInput([formName, 'remarks'], obj.remarks);
13
+ cy.GLO_addInput([formName, 'outbound_stop_definition'], obj.outboundStopDefinition);
14
+ // Stop and handling have no code column, so they are selector components rather than valuelists.
15
+ cy.GLO_popupWindow([formName, 'outbound_stop_id'], obj.outboundStop);
16
+ cy.GLO_popupWindow([formName, 'outbound_handling_id'], obj.outboundHandling);
17
+ cy.GLO_addInput([formName, 'outbound_document_type_id'], obj.outboundDocumentType);
18
+ cy.GLO_checkbox([formName, 'flag_outbound_ok'], obj.flagOutboundOk);
19
+ cy.GLO_addInput([formName, 'inbound_stop_definition'], obj.inboundStopDefinition);
20
+ cy.GLO_popupWindow([formName, 'inbound_stop_id'], obj.inboundStop);
21
+ cy.GLO_popupWindow([formName, 'inbound_handling_id'], obj.inboundHandling);
22
+ cy.GLO_addInput([formName, 'inbound_document_type_id'], obj.inboundDocumentType);
23
+ cy.GLO_checkbox([formName, 'flag_inbound_ok'], obj.flagInboundOk);
24
+ };
@@ -0,0 +1,11 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objConsignmentCustomsSettings = {
3
+ flagCustoms?: CheckState;
4
+ customsFlow?: string;
5
+ gmr?: string;
6
+ consignor?: string;
7
+ consignorAddress?: string;
8
+ consignee?: string;
9
+ consigneeAddress?: string;
10
+ };
11
+ export declare const GLO_enterConsignmentCustomsSettings: (obj: objConsignmentCustomsSettings) => void;
@@ -0,0 +1,11 @@
1
+ export const GLO_enterConsignmentCustomsSettings = (obj) => {
2
+ const formName = 'ng$consignment_dtl_customs';
3
+ cy.GLO_checkbox([formName, 'flag_customs'], obj.flagCustoms);
4
+ cy.GLO_addInput([formName, 'customs_flow_id'], obj.customsFlow);
5
+ cy.GLO_addInput([formName, 'gmr'], obj.gmr);
6
+ // The four party fields are selector components, so they open a selection window instead of a dropdown.
7
+ cy.GLO_popupWindow([formName, 'consignor_id'], obj.consignor);
8
+ cy.GLO_popupWindow([formName, 'consignor_address'], obj.consignorAddress);
9
+ cy.GLO_popupWindow([formName, 'consignee_id'], obj.consignee);
10
+ cy.GLO_popupWindow([formName, 'consignee_address'], obj.consigneeAddress);
11
+ };
@@ -0,0 +1,22 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objCustomsDeclaration = {
3
+ consignmentCustomsSequenceNr?: number;
4
+ customsType?: string;
5
+ transportMethod?: string;
6
+ deliveryCondition?: string;
7
+ emergencyProcedureCode?: string;
8
+ flagBlocked?: CheckState;
9
+ consignor?: string;
10
+ consignorAddress?: string;
11
+ consignee?: string;
12
+ consigneeAddress?: string;
13
+ customsReference?: string;
14
+ externalReference?: string;
15
+ traderReference?: string;
16
+ supplierReference?: string;
17
+ ensReference?: string;
18
+ customsPoao?: string;
19
+ customsCrn?: string;
20
+ sequenceNrLine?: number;
21
+ };
22
+ export declare const GLO_enterCustomsDeclaration: (obj: objCustomsDeclaration) => void;
@@ -0,0 +1,22 @@
1
+ export const GLO_enterCustomsDeclaration = (obj) => {
2
+ const formName = 'ng$customs_declaration_dtl';
3
+ cy.GLO_addInput([formName, 'consignment_customs_sequence_nr'], obj.consignmentCustomsSequenceNr);
4
+ cy.GLO_addInput([formName, 'customs_type'], obj.customsType);
5
+ cy.GLO_addInput([formName, 'transport_method_id'], obj.transportMethod);
6
+ cy.GLO_addInput([formName, 'deliverycondition_id'], obj.deliveryCondition);
7
+ cy.GLO_addInput([formName, 'emergency_procedure_code'], obj.emergencyProcedureCode);
8
+ cy.GLO_checkbox([formName, 'flag_blocked'], obj.flagBlocked);
9
+ // The four party fields are selector components, so they open a selection window instead of a dropdown.
10
+ cy.GLO_popupWindow([formName, 'consignor_id'], obj.consignor);
11
+ cy.GLO_popupWindow([formName, 'consignor_address'], obj.consignorAddress);
12
+ cy.GLO_popupWindow([formName, 'consignee_id'], obj.consignee);
13
+ cy.GLO_popupWindow([formName, 'consignee_address'], obj.consigneeAddress);
14
+ cy.GLO_addInput([formName, 'customs_reference'], obj.customsReference);
15
+ cy.GLO_addInput([formName, 'external_reference'], obj.externalReference);
16
+ cy.GLO_addInput([formName, 'trader_reference'], obj.traderReference);
17
+ cy.GLO_addInput([formName, 'supplier_reference'], obj.supplierReference);
18
+ cy.GLO_addInput([formName, 'ens_reference'], obj.ensReference);
19
+ cy.GLO_addInput([formName, 'customs_poao'], obj.customsPoao);
20
+ cy.GLO_addInput([formName, 'customs_crn'], obj.customsCrn);
21
+ cy.GLO_addInput([formName, 'sequence_nr_line'], obj.sequenceNrLine);
22
+ };
@@ -0,0 +1,20 @@
1
+ export type objCustomsDeclarationDetail = {
2
+ intrastatCode?: string;
3
+ intrastatCodeReceived?: string;
4
+ customsDescription?: string;
5
+ customsCountry?: string;
6
+ customsRegionCode?: string;
7
+ dgnUnNumber?: string;
8
+ sscc?: string;
9
+ packagingArticleUnit?: string;
10
+ customsPackagingCode?: string;
11
+ packagingQuantity?: number;
12
+ extraCustomsPackagingCode?: string;
13
+ extraPackagingQuantity?: number;
14
+ grossWeight?: number;
15
+ netWeight?: number;
16
+ weightUnit?: string;
17
+ invoiceAmount?: number;
18
+ currency?: string;
19
+ };
20
+ export declare const GLO_enterCustomsDeclarationDetail: (obj: objCustomsDeclarationDetail) => void;
@@ -0,0 +1,20 @@
1
+ export const GLO_enterCustomsDeclarationDetail = (obj) => {
2
+ const formName = 'ng$customs_declaration_detail_dtl';
3
+ cy.GLO_addInput([formName, 'intrastat_code_id'], obj.intrastatCode);
4
+ cy.GLO_addInput([formName, 'intrastat_code_received'], obj.intrastatCodeReceived);
5
+ cy.GLO_addInput([formName, 'customs_description'], obj.customsDescription);
6
+ cy.GLO_addInput([formName, 'customs_country_id'], obj.customsCountry);
7
+ cy.GLO_addInput([formName, 'customs_region_code'], obj.customsRegionCode);
8
+ cy.GLO_addInput([formName, 'dgn_un_number_id'], obj.dgnUnNumber);
9
+ cy.GLO_addInput([formName, 'sscc'], obj.sscc);
10
+ cy.GLO_addInput([formName, 'packaging_articleunit_id'], obj.packagingArticleUnit);
11
+ cy.GLO_addInput([formName, 'customs_packaging_code'], obj.customsPackagingCode);
12
+ cy.GLO_addInput([formName, 'packaging_quantity'], obj.packagingQuantity);
13
+ cy.GLO_addInput([formName, 'extra_customs_packaging_code'], obj.extraCustomsPackagingCode);
14
+ cy.GLO_addInput([formName, 'extra_packaging_quantity'], obj.extraPackagingQuantity);
15
+ cy.GLO_addInput([formName, 'gross_weight'], obj.grossWeight);
16
+ cy.GLO_addInput([formName, 'net_weight'], obj.netWeight);
17
+ cy.GLO_addInput([formName, 'weight_unit_id'], obj.weightUnit);
18
+ cy.GLO_addInput([formName, 'invoice_amount'], obj.invoiceAmount);
19
+ cy.GLO_addInput([formName, 'currency_id'], obj.currency);
20
+ };
@@ -0,0 +1,9 @@
1
+ export type objCustomsDeclarationDocument = {
2
+ documentType?: string;
3
+ customsType?: string;
4
+ customsOfficeDestinationAddress?: string;
5
+ customsMrn?: string;
6
+ customsUcr?: string;
7
+ customsLrn?: string;
8
+ };
9
+ export declare const GLO_enterCustomsDeclarationDocument: (obj: objCustomsDeclarationDocument) => void;
@@ -0,0 +1,9 @@
1
+ export const GLO_enterCustomsDeclarationDocument = (obj) => {
2
+ const formName = 'ng$customs_declaration_document_dtl';
3
+ cy.GLO_addInput([formName, 'document_type_id'], obj.documentType);
4
+ cy.GLO_addInput([formName, 'customs_type'], obj.customsType);
5
+ cy.GLO_popupWindow([formName, 'customs_office_destination_address_id'], obj.customsOfficeDestinationAddress);
6
+ cy.GLO_addInput([formName, 'customs_mrn'], obj.customsMrn);
7
+ cy.GLO_addInput([formName, 'customs_ucr'], obj.customsUcr);
8
+ cy.GLO_addInput([formName, 'customs_lrn'], obj.customsLrn);
9
+ };
@@ -0,0 +1,22 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objLogisticContractCustomsDefinition = {
3
+ sequenceNr?: number;
4
+ customsType?: string;
5
+ customsResponsibility?: string;
6
+ customsRelation?: string;
7
+ customerCustomsBroker?: string;
8
+ customsOfficeDefinition?: string;
9
+ customsOfficeAddress?: string;
10
+ transportScenario?: string;
11
+ outboundStopDefinition?: string;
12
+ outboundStop?: string;
13
+ outboundHandling?: string;
14
+ outboundDocumentType?: string;
15
+ inboundStopDefinition?: string;
16
+ inboundStop?: string;
17
+ inboundHandling?: string;
18
+ inboundDocumentType?: string;
19
+ flagWeekendClearance?: CheckState;
20
+ remarks?: string;
21
+ };
22
+ export declare const GLO_enterLogisticContractCustomsDefinition: (obj: objLogisticContractCustomsDefinition) => void;
@@ -0,0 +1,22 @@
1
+ export const GLO_enterLogisticContractCustomsDefinition = (obj) => {
2
+ const formName = 'ng$logistic_contract_customs_definition_dtl';
3
+ cy.GLO_addInput([formName, 'sequence_nr'], obj.sequenceNr);
4
+ cy.GLO_addInput([formName, 'customs_type'], obj.customsType);
5
+ cy.GLO_addInput([formName, 'customs_responsibility'], obj.customsResponsibility);
6
+ cy.GLO_popupWindow([formName, 'customs_relation_id'], obj.customsRelation);
7
+ cy.GLO_addInput([formName, 'customer_customs_broker'], obj.customerCustomsBroker);
8
+ cy.GLO_addInput([formName, 'customs_office_definition'], obj.customsOfficeDefinition);
9
+ cy.GLO_popupWindow([formName, 'customs_office_address_id'], obj.customsOfficeAddress);
10
+ cy.GLO_addInput([formName, 'lcd_tpt_scenario_execution_id'], obj.transportScenario);
11
+ cy.GLO_addInput([formName, 'outbound_stop_definition'], obj.outboundStopDefinition);
12
+ // Stop and handling have no code column, so they are selector components rather than valuelists.
13
+ cy.GLO_popupWindow([formName, 'outbound_stop_id'], obj.outboundStop);
14
+ cy.GLO_popupWindow([formName, 'outbound_handling_id'], obj.outboundHandling);
15
+ cy.GLO_addInput([formName, 'outbound_document_type_id'], obj.outboundDocumentType);
16
+ cy.GLO_addInput([formName, 'inbound_stop_definition'], obj.inboundStopDefinition);
17
+ cy.GLO_popupWindow([formName, 'inbound_stop_id'], obj.inboundStop);
18
+ cy.GLO_popupWindow([formName, 'inbound_handling_id'], obj.inboundHandling);
19
+ cy.GLO_addInput([formName, 'inbound_document_type_id'], obj.inboundDocumentType);
20
+ cy.GLO_checkbox([formName, 'flag_weekend_clearance'], obj.flagWeekendClearance);
21
+ cy.GLO_addInput([formName, 'remarks'], obj.remarks);
22
+ };
@@ -0,0 +1,11 @@
1
+ import type { CheckState } from '../tsHelper.js';
2
+ export type objLogisticContractDetailCustoms = {
3
+ flagCustoms?: CheckState;
4
+ customsFlow?: string;
5
+ eoriEidr?: string;
6
+ consignor?: string;
7
+ consignorAddress?: string;
8
+ consignee?: string;
9
+ consigneeAddress?: string;
10
+ };
11
+ export declare const GLO_enterLogisticContractDetailCustoms: (obj: objLogisticContractDetailCustoms) => void;
@@ -0,0 +1,11 @@
1
+ export const GLO_enterLogisticContractDetailCustoms = (obj) => {
2
+ const formName = 'ng$logistic_contract_detail_dtl_customs';
3
+ cy.GLO_checkbox([formName, 'flag_customs'], obj.flagCustoms);
4
+ cy.GLO_addInput([formName, 'customs_flow_id'], obj.customsFlow);
5
+ cy.GLO_addInput([formName, 'eori_eidr'], obj.eoriEidr);
6
+ // The four party fields are selector components, so they open a selection window instead of a dropdown.
7
+ cy.GLO_popupWindow([formName, 'consignor_id'], obj.consignor);
8
+ cy.GLO_popupWindow([formName, 'consignor_address'], obj.consignorAddress);
9
+ cy.GLO_popupWindow([formName, 'consignee_id'], obj.consignee);
10
+ cy.GLO_popupWindow([formName, 'consignee_address'], obj.consigneeAddress);
11
+ };
@@ -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);
@@ -183,6 +183,8 @@ declare global {
183
183
  /**
184
184
  * Command to select a tab using its data-cy attribute, which is composed of formName.elementName
185
185
  *
186
+ * Fails if the tab has to be opened or closed but is disabled.
187
+ *
186
188
  * @param {string[]} selector - One element: `[name=]` lookup. Two elements: `[data-cy$="form.element"]` lookup.
187
189
  * @param {string} tabName - The name of the tab to be selected.
188
190
  * @param {OpenClose} openOrClose - Indicates whether to open (true) or close (false) the tab.
@@ -647,6 +649,14 @@ declare global {
647
649
  * @param {disabledFlag} [shouldBeEnabled=true] - Whether the field should be enabled (true) or disabled (false).
648
650
  */
649
651
  GLO_checkFieldEnabled(selector: string[], shouldBeEnabled?: disabledFlag): void;
652
+ /**
653
+ * Checks if a tab is enabled or disabled, without clicking it.
654
+ *
655
+ * @param {string[]} selector - One element: `[name=]` lookup. Two elements: `[data-cy$="form.element"]` lookup.
656
+ * @param {string} tabName - The name of the tab to check.
657
+ * @param {disabledFlag} [shouldBeEnabled=true] - Whether the tab should be enabled (true) or disabled (false).
658
+ */
659
+ GLO_checkTabEnabled(selector: string[], tabName: string, shouldBeEnabled?: disabledFlag): void;
650
660
  /**
651
661
  * Checks if a field has the given background color class applied to its input element.
652
662
  *
@@ -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!');
@@ -693,12 +693,12 @@ Cypress.Commands.add('GLO_closeDashboard', () => {
693
693
  if (testFailed)
694
694
  return;
695
695
  cy.get('.navigation-item-close', { timeout: 10000 }).click();
696
- cy.GLO_getVisible('.svy-message-dialog', 30000).should('exist');
697
- 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) => {
698
698
  expect($dialog.find('.window-title').text().trim()).to.match(/^(Confirm|Bevestigen)$/);
699
699
  expect($dialog.find('.window-body').text().trim()).to.match(/^(Close all programs\?|Sluit alle programma's\?)$/);
700
700
  });
701
- cy.get('.svy-message-dialog')
701
+ cy.get('.svy-message-dialog.active')
702
702
  .find('.svy-btn-primary')
703
703
  .contains(/^(Yes|Ja)$/)
704
704
  .should('exist')
@@ -713,6 +713,12 @@ Cypress.Commands.add('GLO_selectTab', (selector, tabName, openOrClose) => {
713
713
  const isOpenAttr = $tab[0].getAttribute('open'); // Attribute is a string 'true' or missing -> null
714
714
  const isOpen = isOpenAttr === 'true'; // Convert to boolean
715
715
  if (openOrClose !== isOpen) {
716
+ // The tab header is a div, so Cypress's `:disabled` actionability check doesn't apply: the click goes through
717
+ // and the server silently ignores it. Closing a disabled tab that is open fails too, because the close request
718
+ // goes through the same click on the same disabled tab, which the server ignores the same way.
719
+ if ($tab[0].hasAttribute('disabled')) {
720
+ throw new Error(`Tab "${tabName}" in ${selector.join('.')} is disabled and cannot be ${openOrClose ? 'opened' : 'closed'}`);
721
+ }
716
722
  const action = openOrClose ? 'Opening' : 'Closing';
717
723
  cy.log(`${action} tab ${tabName}`);
718
724
  cy.wrap($tab).click();
@@ -774,22 +780,23 @@ Cypress.Commands.add('GLO_dialogChoice', (confirmation) => {
774
780
  cy.wait(500);
775
781
  cy.get('body').then(($body) => {
776
782
  let CSSSearchStr;
777
- if ($body.find('.svy-message-dialog').length > 0) {
778
- CSSSearchStr = '.svy-message-dialog button';
783
+ if ($body.find('.svy-message-dialog.active').length > 0) {
784
+ CSSSearchStr = '.svy-message-dialog.active button';
779
785
  }
780
786
  else if ($body.find('.servoycore-dialog-body').length > 0) {
781
787
  // Previous version
782
788
  CSSSearchStr = '.servoycore-dialog-body button';
783
789
  }
784
- else if ($body.find('.svy-dialog.window').length > 0) {
790
+ else if ($body.find('.svy-dialog.window.active').length > 0) {
785
791
  // Another variant
786
- CSSSearchStr = '.svy-dialog.window globiscomponents-button';
792
+ CSSSearchStr = '.svy-dialog.window.active globiscomponents-button';
787
793
  }
788
794
  else {
789
795
  cy.GLO_log('Popup not found, skipping dialog option');
790
796
  return;
791
797
  }
792
- if ($body.find('.window-title.type-error').length > 0 && $body.find('.window-title.type-error').is(':visible')) {
798
+ const $errorTitle = $body.find('.svy-message-dialog.active .window-title.type-error, .svy-dialog.window.active .window-title.type-error');
799
+ if ($errorTitle.length > 0 && $errorTitle.is(':visible')) {
793
800
  cy.GLO_log('Error dialog, failing test. use GLO_dialogRequiredCheck.');
794
801
  cy.wrap(null).should(() => {
795
802
  throw new Error('Error dialog, failing test. use GLO_dialogRequiredCheck.');
@@ -816,7 +823,7 @@ Cypress.Commands.add('GLO_dialogChoice', (confirmation) => {
816
823
  });
817
824
  });
818
825
  Cypress.Commands.add('GLO_addInputDialog', (text) => {
819
- cy.get('.svy-message-dialog .window-body form', { timeout: 15000 }).then(($form) => {
826
+ cy.get('.svy-message-dialog.active .window-body form', { timeout: 15000 }).then(($form) => {
820
827
  const inputType = $form.children().first().prop('tagName').toLowerCase();
821
828
  if (inputType === 'input') {
822
829
  cy.wrap($form).find('input').clear().type(text);
@@ -1000,7 +1007,7 @@ Cypress.Commands.add('GLO_switchCompany', (companyName) => {
1000
1007
  cy.GLO_getElement(['ng$main_form2', 'navbar_top']).find('#userroles-dropdown').click();
1001
1008
  cy.get('.dx-submenu').find(span).contains('Switch company').click();
1002
1009
  cy.GLO_checkLoadingIndicator();
1003
- cy.get('.svy-dialog').find('.dx-dropdowneditor-icon').click();
1010
+ cy.get('.svy-dialog.active').find('.dx-dropdowneditor-icon').click();
1004
1011
  cy.GLO_getVisible('.dx-scrollview-content').contains(companyName).click();
1005
1012
  cy.GLO_clickButton(['ng$login_companies_dlg', 'btn_save']);
1006
1013
  });
@@ -1140,7 +1147,7 @@ const dblClickRowInGrid = (type, value, columnId, equals) => {
1140
1147
  };
1141
1148
  const helper = (filterSelector) => {
1142
1149
  if (columnId) {
1143
- cy.get('[row-index]')
1150
+ cy.get('[row-index]:visible')
1144
1151
  .find(`[col-id="${columnId}"]`)
1145
1152
  .filter(filterSelector, { timeout: 10000 })
1146
1153
  .parentsUntil('.ag-center-cols-container')
@@ -1149,7 +1156,7 @@ const dblClickRowInGrid = (type, value, columnId, equals) => {
1149
1156
  .then(saveValues);
1150
1157
  }
1151
1158
  else {
1152
- cy.get('[row-index]').filter(filterSelector, { timeout: 10000 }).first().then(saveValues);
1159
+ cy.get('[row-index]:visible').filter(filterSelector, { timeout: 10000 }).first().then(saveValues);
1153
1160
  }
1154
1161
  };
1155
1162
  cy.GLO_checkAGLoader();
@@ -1210,12 +1217,12 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1210
1217
  const loadingColumnId = () => columnId
1211
1218
  ? cy.wrap(columnId)
1212
1219
  : cy
1213
- .get('.window-body:visible .ag-center-cols-container [row-index="0"] [role="gridcell"]', { timeout: 30000 })
1220
+ .get('.svy-dialog.active .window-body .ag-center-cols-container [row-index="0"] [role="gridcell"]', { timeout: 30000 })
1214
1221
  .filter((_, el) => Cypress.$(el).text().trim().length > 0)
1215
1222
  .first()
1216
1223
  .invoke('attr', 'col-id');
1217
1224
  // So we dont have to repeat the whole command everywhere, just looks better in code below.
1218
- const searchData = () => loadingColumnId().then((colId) => cy.get(`.window-body [row-index] [col-id="${colId}"]`, { timeout: loadTimeout }).should(($els) => {
1225
+ const searchData = () => loadingColumnId().then((colId) => cy.get(`.svy-dialog.active .window-body [row-index] [col-id="${colId}"]`, { timeout: loadTimeout }).should(($els) => {
1219
1226
  assert.equal($els.toArray().filter((item) => (item.textContent || '').trim() === '').length, 0, 'Grid cells should not have empty values');
1220
1227
  assert.isTrue(Cypress.$('.ag-loading').length === 0 && Cypress.$('loading-indicator').length === 0, '(Grid) loadingindicator is not visible');
1221
1228
  }));
@@ -1249,7 +1256,7 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1249
1256
  searchData()
1250
1257
  .then(($els) => Array.from($els, (el) => el.textContent.trim()))
1251
1258
  .as('beforeSearch');
1252
- cy.GLO_getVisible('.window-body').within(() => {
1259
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1253
1260
  cy.GLO_getVisible(`.ag-header-cell[col-id="${columnId}"]`)
1254
1261
  .invoke('attr', 'aria-colindex')
1255
1262
  .then((ariaColIndex) => {
@@ -1268,13 +1275,13 @@ const selectFromPopupWindow = (value, columnId, equals = noEqualBool) => {
1268
1275
  }
1269
1276
  });
1270
1277
  }
1271
- cy.GLO_getVisible('.window-body').within(() => {
1278
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1272
1279
  dblClickRowInGrid(span, value, columnId, equals);
1273
1280
  });
1274
1281
  }
1275
1282
  else {
1276
1283
  cy.log('Value already exists in the grid, skipping search.');
1277
- cy.GLO_getVisible('.window-body').within(() => {
1284
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
1278
1285
  dblClickRowInGrid(span, value, columnId, equals);
1279
1286
  });
1280
1287
  }
@@ -1302,7 +1309,7 @@ Cypress.Commands.add('GLO_popupWindow', (selector, value, columnId, equals = noE
1302
1309
  cy.GLO_checkLoadingIndicator();
1303
1310
  }
1304
1311
  cy.get('body').then(($body) => {
1305
- if ($body.find('.window-body').filter(':visible').length > 0) {
1312
+ if ($body.find('.svy-dialog.active .window-body').length > 0) {
1306
1313
  selectFromPopupWindow(value, columnId, equals);
1307
1314
  }
1308
1315
  else {
@@ -2142,6 +2149,11 @@ Cypress.Commands.add('GLO_checkFieldEnabled', (selector, shouldBeEnabled = enabl
2142
2149
  }
2143
2150
  });
2144
2151
  });
2152
+ Cypress.Commands.add('GLO_checkTabEnabled', (selector, tabName, shouldBeEnabled = enabled) => {
2153
+ cy.GLO_getElement(selector)
2154
+ .contains('[tabcontainer] .tab', tabName)
2155
+ .should(shouldBeEnabled ? 'not.have.attr' : 'have.attr', 'disabled');
2156
+ });
2145
2157
  Cypress.Commands.add('GLO_checkFieldBgColor', (selector, color = 'red') => {
2146
2158
  const [formName, fieldOrFields] = selector;
2147
2159
  const fields = Array.isArray(fieldOrFields) ? fieldOrFields : [fieldOrFields];
@@ -2289,7 +2301,12 @@ Cypress.Commands.add('GLO_extendedFilter', (filter, clearExistingFilters) => {
2289
2301
  cy.wrap($field).find('button.fa-search').click();
2290
2302
  cy.GLO_getElement(['svy_nav_selection', 'search_criteria']).type(String(v));
2291
2303
  cy.GLO_clickButton(['svy_nav_selection', 'btn_search']);
2292
- dblClickRowInGrid(span, String(v));
2304
+ // dblClickRowInGrid is document-scoped unless wrapped in .within() - without this,
2305
+ // its unscoped [row-index] lookup can match a row in whatever grid sits behind this
2306
+ // dialog instead of the dialog's own grid. Same scoping GLO_popupWindow uses.
2307
+ cy.GLO_getVisible('.svy-dialog.active .window-body').within(() => {
2308
+ dblClickRowInGrid(span, String(v));
2309
+ });
2293
2310
  cy.GLO_checkLoadingIndicator();
2294
2311
  }
2295
2312
  else {
@@ -2311,8 +2328,6 @@ Cypress.Commands.add('GLO_extendedFilter', (filter, clearExistingFilters) => {
2311
2328
  // Fall through
2312
2329
  case 'before':
2313
2330
  case 'after':
2314
- case '<= today +/- days':
2315
- case '>= today +/- days':
2316
2331
  case 'contains':
2317
2332
  case 'starts with':
2318
2333
  case 'ends with':
@@ -2322,6 +2337,32 @@ Cypress.Commands.add('GLO_extendedFilter', (filter, clearExistingFilters) => {
2322
2337
  case '<=':
2323
2338
  filterComponent().find(getFilterFieldSelector(path, 'value')).type(`{selectall}{del}${v}{enter}`);
2324
2339
  return;
2340
+ case '<= today +/- days':
2341
+ case '>= today +/- days': {
2342
+ // This operator renders two separate controls (a sign select + a days number box),
2343
+ // not the single 'value' input the other operators share - getFilterFieldSelector's
2344
+ // generic 'value' selector doesn't match either one.
2345
+ const num = Number(v);
2346
+ const signInput = () => filterComponent().find(`[data-cy="filter-builder-xdays-sign-${path}"] .dx-texteditor-input`);
2347
+ signInput().then(($el) => {
2348
+ const id = $el[0].getAttribute('aria-controls');
2349
+ if (!id)
2350
+ cy.wrap($el).click();
2351
+ });
2352
+ signInput().then(($el) => {
2353
+ const id = $el[0].getAttribute('aria-controls');
2354
+ if (!id)
2355
+ throw new Error('Missing aria-controls');
2356
+ cy.get(`[id="${id}"] .dx-scrollview-content`)
2357
+ .children()
2358
+ .contains(num < 0 ? '-' : '+')
2359
+ .click();
2360
+ });
2361
+ filterComponent()
2362
+ .find(`[data-cy="filter-builder-xdays-value-${path}"] .dx-texteditor-input`)
2363
+ .type(`{selectall}{del}${Math.abs(num)}{enter}`);
2364
+ return;
2365
+ }
2325
2366
  default: {
2326
2367
  const _exhaustive = operator;
2327
2368
  throw new Error(`Unhandled operator: ${String(_exhaustive)}`);
@@ -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
  })
@@ -35,6 +35,10 @@ let lastCypressLocator = '';
35
35
  let lastGLOCommand = '';
36
36
  let lastActionStartTime = null;
37
37
  let wsInteractionCounter = 0;
38
+ // Module-scoped (not per-WS_loadingCheck-invocation): a componentApi call's reply can arrive after
39
+ // the invocation that recorded its smsgid has already resolved, so tracking has to survive across
40
+ // invocations within a test — cleared per-test in beforeEach below, same as the other cross-invocation state.
41
+ const componentApiSmsgids = new Set();
38
42
  Cypress.on('command:start', (cmd) => {
39
43
  const rawName = cmd.get('name');
40
44
  const name = typeof rawName === 'string' ? rawName : '';
@@ -57,6 +61,7 @@ beforeEach(() => {
57
61
  lastGLOCommand = '';
58
62
  lastActionStartTime = null;
59
63
  wsInteractionCounter = 0;
64
+ componentApiSmsgids.clear();
60
65
  });
61
66
  after(() => {
62
67
  cy.task('flushWsMetrics', null, { log: false }).then((result) => {
@@ -828,17 +833,31 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
828
833
  debugLog('🔁 Ignored columnState-only dataPush');
829
834
  return;
830
835
  }
836
+ // Server-initiated componentApi calls (e.g. internalGetColumnState) carry their own smsgid the
837
+ // client will echo back in its reply - record it so checkForDialogInteraction can recognize that
838
+ // reply as unrelated to a dialog, rather than mistaking it for one (both share the {smsgid, ret}
839
+ // wire shape).
840
+ const componentApisField = data.componentApis;
841
+ if (Array.isArray(componentApisField) && 'smsgid' in data && typeof data.smsgid === 'number') {
842
+ componentApiSmsgids.add(data.smsgid);
843
+ }
831
844
  const hasDialog = 'serviceApis' in data && Array.isArray(data.serviceApis) && checkForDialogInResponse(data.serviceApis);
845
+ // Response triggers a dialog — hold the triggering request until the dialog is dismissed.
846
+ // The held id is threaded through to updateDialogState so the dialog record is attributed to
847
+ // the exact request that was held, instead of updateDialogState independently re-deriving it
848
+ // from state.lastOutgoingRequestId (which can point at a different, possibly already-resolved
849
+ // request when several requests are in flight - leaking the hold forever, since dismissal only
850
+ // releases a held request whose id matches the dialog's recorded triggeringRequestId exactly).
851
+ let heldRequestId = null;
832
852
  if (hasDialog) {
833
- // Response triggers a dialog — hold the triggering request until the dialog is dismissed
834
- handleDialogResponse(data);
853
+ heldRequestId = handleDialogResponse(data);
835
854
  }
836
855
  else {
837
856
  handleNormalResponse(data);
838
857
  }
839
858
  if ('serviceApis' in data && Array.isArray(data.serviceApis)) {
840
859
  updateLoadingState(data.serviceApis);
841
- updateDialogState(data.serviceApis);
860
+ updateDialogState(data.serviceApis, heldRequestId);
842
861
  updateExpectFormShowState(data.serviceApis);
843
862
  }
844
863
  // Check if this response echoes back a pending svyPush data change
@@ -889,6 +908,10 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
889
908
  function checkForDialogInteraction(data) {
890
909
  if (!('smsgid' in data && 'ret' in data && typeof data.ret === 'string'))
891
910
  return;
911
+ // This reply answers a componentApi call (e.g. internalGetColumnState), not a dialog button -
912
+ // it just happens to share the same {smsgid, ret} wire shape. Ignore it here.
913
+ if (typeof data.smsgid === 'number' && componentApiSmsgids.delete(data.smsgid))
914
+ return;
892
915
  state.extendedQuietPeriod = true;
893
916
  // Release the oldest held non-Window pending dialog (FIFO order)
894
917
  for (const [key, dialogData] of pendingDialogs) {
@@ -955,6 +978,8 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
955
978
  /**
956
979
  * When a response arrives that also opens a dialog, the triggering request is held
957
980
  * rather than resolved. The actual result will arrive after the dialog is dismissed.
981
+ * Returns the held request id so the caller can attribute the dialog record to this exact
982
+ * request (see the comment in handleIncomingMessage for why that matters).
958
983
  */
959
984
  function handleDialogResponse(data) {
960
985
  let targetId = null;
@@ -971,6 +996,7 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
971
996
  state.heldRequests.add(targetId);
972
997
  debugLog(`🔲 Dialog detected - holding request: ${targetId}`);
973
998
  }
999
+ return targetId;
974
1000
  }
975
1001
  function wsRequestPhase(sendTs) {
976
1002
  // No loading detected yet at response time → pre_loading
@@ -1180,10 +1206,10 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
1180
1206
  });
1181
1207
  }
1182
1208
  /** Dispatches serviceApi calls to the appropriate dialog/window handler. */
1183
- function updateDialogState(apis) {
1209
+ function updateDialogState(apis, heldRequestId = null) {
1184
1210
  apis.forEach((api) => {
1185
1211
  if (api.name === 'dialogs')
1186
- handleMessageDialog(api);
1212
+ handleMessageDialog(api, heldRequestId);
1187
1213
  if (api.name === '$windowService')
1188
1214
  handleWindowDialog(api);
1189
1215
  if (api.name === 'window' && api.call === 'cancelFormPopupInternal')
@@ -1212,8 +1238,14 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
1212
1238
  * Only Info dialogs with the literal title "Info" are auto-handled and block the loader.
1213
1239
  * All others (Warning, Error, Input, Select, Info with a different title) use a "Question:"
1214
1240
  * key which unblocks the loader so the test can interact with the dialog.
1241
+ *
1242
+ * triggeringRequestId must be the exact request id handleDialogResponse held for this same
1243
+ * incoming message (passed in as heldRequestId), not independently re-derived from
1244
+ * state.lastOutgoingRequestId - that can point at a different (or already-resolved) request
1245
+ * when several requests are in flight, which would leave the actually-held request unreleasable
1246
+ * once the dialog is dismissed and permanently hang the loader.
1215
1247
  */
1216
- function handleMessageDialog(api) {
1248
+ function handleMessageDialog(api, heldRequestId = null) {
1217
1249
  if (!api.call || !DIALOG_METHODS.includes(api.call))
1218
1250
  return;
1219
1251
  const dialogType = api.call.replace('show', '').replace('Dialog', '');
@@ -1221,7 +1253,7 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
1221
1253
  // 'Info' key → loader blocks until auto-handled; 'Question' key → loader unblocks immediately
1222
1254
  const keyType = dialogType === 'Info' && wsTitle === 'Info' ? 'Info' : 'Question';
1223
1255
  const dialogKey = `${keyType}:${++dialogIdCounter}`;
1224
- pendingDialogs.set(dialogKey, { dialogType, triggeringRequestId: state.lastOutgoingRequestId, timestamp: Date.now() });
1256
+ pendingDialogs.set(dialogKey, { dialogType, triggeringRequestId: heldRequestId, timestamp: Date.now() });
1225
1257
  debugLog(`🔲 ${dialogType} dialog opened: ${api.call}`, { ...createDialogDebugInfo(api), dialogKey, wsTitle });
1226
1258
  }
1227
1259
  /** Routes $windowService API calls to create/show/destroy handlers. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globisnv/cypress-core",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Core building blocks for Globis Cypress E2E tests",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",