@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.
- package/dist/blueprints/GLO_consignment.js +1 -0
- package/dist/blueprints/GLO_consignmentCustoms.d.ts +24 -0
- package/dist/blueprints/GLO_consignmentCustoms.js +24 -0
- package/dist/blueprints/GLO_consignmentCustomsSettings.d.ts +11 -0
- package/dist/blueprints/GLO_consignmentCustomsSettings.js +11 -0
- package/dist/blueprints/GLO_customsDeclaration.d.ts +22 -0
- package/dist/blueprints/GLO_customsDeclaration.js +22 -0
- package/dist/blueprints/GLO_customsDeclarationDetail.d.ts +20 -0
- package/dist/blueprints/GLO_customsDeclarationDetail.js +20 -0
- package/dist/blueprints/GLO_customsDeclarationDocument.d.ts +9 -0
- package/dist/blueprints/GLO_customsDeclarationDocument.js +9 -0
- package/dist/blueprints/GLO_logisticContractCustomsDefinition.d.ts +22 -0
- package/dist/blueprints/GLO_logisticContractCustomsDefinition.js +22 -0
- package/dist/blueprints/GLO_logisticContractDetailCustoms.d.ts +11 -0
- package/dist/blueprints/GLO_logisticContractDetailCustoms.js +11 -0
- package/dist/blueprints/GLO_relations.js +4 -4
- package/dist/commands/GLO_commands.d.ts +10 -0
- package/dist/commands/GLO_commands.js +63 -22
- package/dist/commands/GLT_commands.js +2 -2
- package/dist/websocketDetector.js +39 -7
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
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').
|
|
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
|
|
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
|
-
|
|
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:
|
|
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. */
|