@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.
- package/biome.json +11 -0
- 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_dangerousGoods.d.ts +10 -0
- package/dist/blueprints/GLO_dangerousGoods.js +9 -0
- package/dist/blueprints/GLO_logisticContract.d.ts +14 -0
- package/dist/blueprints/GLO_logisticContract.js +53 -11
- package/dist/blueprints/GLO_logisticContractCustomsDefinition.d.ts +22 -0
- package/dist/blueprints/GLO_logisticContractCustomsDefinition.js +22 -0
- package/dist/blueprints/GLO_logisticContractDetail.d.ts +11 -1
- package/dist/blueprints/GLO_logisticContractDetail.js +33 -8
- package/dist/blueprints/GLO_logisticContractDetailCustoms.d.ts +11 -0
- package/dist/blueprints/GLO_logisticContractDetailCustoms.js +11 -0
- package/dist/blueprints/GLO_logisticContractDetailHandling.d.ts +1 -0
- package/dist/blueprints/GLO_logisticContractDetailHandling.js +1 -0
- package/dist/blueprints/GLO_logisticContractDetailItem.js +1 -1
- package/dist/blueprints/GLO_logisticContractItem.d.ts +9 -0
- package/dist/blueprints/GLO_logisticContractItem.js +31 -4
- package/dist/blueprints/GLO_logisticLocationType.d.ts +4 -0
- package/dist/blueprints/GLO_logisticLocationType.js +4 -0
- package/dist/blueprints/GLO_planBloc.d.ts +6 -0
- package/dist/blueprints/GLO_planBloc.js +6 -0
- package/dist/blueprints/GLO_priceEntry.d.ts +1 -0
- package/dist/blueprints/GLO_priceEntry.js +1 -0
- package/dist/blueprints/GLO_purchaseOrderLine.js +4 -0
- package/dist/blueprints/GLO_relations.js +4 -4
- package/dist/blueprints/GLO_stopSchedule.d.ts +11 -0
- package/dist/blueprints/GLO_stopSchedule.js +11 -0
- package/dist/blueprints/GLO_transportScenario.d.ts +4 -0
- package/dist/blueprints/GLO_transportScenario.js +4 -0
- package/dist/blueprints/GLO_transportScenarioExecution.d.ts +6 -0
- package/dist/blueprints/GLO_transportScenarioExecution.js +5 -0
- package/dist/blueprints/GLO_transportScenarioRegion.d.ts +5 -0
- package/dist/blueprints/GLO_transportScenarioRegion.js +5 -0
- package/dist/blueprints/GLO_zoneTypeEntityType.d.ts +4 -0
- package/dist/blueprints/GLO_zoneTypeEntityType.js +4 -0
- package/dist/commands/GLO_commands.js +72 -39
- package/dist/commands/GLT_commands.js +2 -2
- package/dist/config.js +4 -2
- package/dist/websocketDetector.js +148 -114
- package/package.json +9 -6
- package/template/.gitattributes +12 -0
- package/template/package.json +5 -2
|
@@ -497,66 +497,86 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
|
|
|
497
497
|
});
|
|
498
498
|
return cy.wrap('locked', { log: false });
|
|
499
499
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Entry point for the resolution loop.
|
|
502
|
+
*
|
|
503
|
+
* The poll body only *reads* synchronous state (`win.wsMessages`, `state`, the AUT DOM via
|
|
504
|
+
* `Cypress.$`), so it runs in a plain `setInterval` inside a single command-queue entry — stack
|
|
505
|
+
* depth stays constant however long we wait. The old loop recursed through `cy.wait().then(checkAndWait)`,
|
|
506
|
+
* nesting one chainer per 100 ms; under the 20-min ceiling that exhausted V8's stack before
|
|
507
|
+
* handleTimeout() could ever fire. Only the dialog auto-click needs queued cy commands, so it is
|
|
508
|
+
* returned as a 'dialog' outcome and performed in a follow-up .then() before re-entering the poll.
|
|
509
|
+
*/
|
|
504
510
|
function waitForResolution() {
|
|
505
511
|
const startTime = Date.now();
|
|
506
|
-
|
|
507
|
-
|
|
512
|
+
// The .then() awaits the poll promise, so its command timeout must outlast the longest
|
|
513
|
+
// possible poll — otherwise Cypress aborts before pollOnce() reaches its own timeout branch.
|
|
514
|
+
const pollCommandTimeout = (options?.timeout ? config.timeout : Math.max(1200000, config.timeout)) + 60000;
|
|
515
|
+
return runPoll();
|
|
516
|
+
function runPoll() {
|
|
508
517
|
return cy
|
|
509
518
|
.window({ log: false })
|
|
510
|
-
.then((win) => {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
519
|
+
.then({ timeout: pollCommandTimeout }, (win) => {
|
|
520
|
+
return new Cypress.Promise((resolve) => {
|
|
521
|
+
let settled = false;
|
|
522
|
+
let intervalId = null;
|
|
523
|
+
const finish = (outcome) => {
|
|
524
|
+
if (settled)
|
|
525
|
+
return;
|
|
526
|
+
settled = true;
|
|
527
|
+
if (intervalId !== null)
|
|
528
|
+
clearInterval(intervalId);
|
|
529
|
+
resolve(outcome);
|
|
530
|
+
};
|
|
531
|
+
const tick = () => {
|
|
532
|
+
try {
|
|
533
|
+
const outcome = pollOnce(win);
|
|
534
|
+
if (outcome)
|
|
535
|
+
finish(outcome);
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
finish({ type: 'error', error });
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
tick(); // check immediately, then every 100 ms
|
|
542
|
+
if (!settled)
|
|
543
|
+
intervalId = setInterval(tick, 100);
|
|
544
|
+
});
|
|
522
545
|
})
|
|
523
|
-
.then((
|
|
524
|
-
if (dialogType === 'navigated') {
|
|
525
|
-
debugLog('🟢 Page navigated away — resolving loader');
|
|
526
|
-
log.set({ displayName: '🟢 WSLoader', message: 'Resolved' }).end();
|
|
527
|
-
return cy.wrap(undefined, { log: false });
|
|
528
|
-
}
|
|
529
|
-
if (dialogType === 'locked') {
|
|
530
|
-
debugLog('🔓 Resolving loader due to locked record detection');
|
|
531
|
-
log.set({ displayName: '🟡 WSLoader', message: 'Resolved (Locked Record)' }).end();
|
|
532
|
-
return clearWebSocketMessages();
|
|
533
|
-
}
|
|
534
|
-
return performResolutionCheck();
|
|
535
|
-
});
|
|
546
|
+
.then((outcome) => dispatch(outcome));
|
|
536
547
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
548
|
+
/** One synchronous poll tick. Returns an outcome to act on, or null to keep polling. */
|
|
549
|
+
function pollOnce(win) {
|
|
550
|
+
let wsMessages;
|
|
551
|
+
try {
|
|
552
|
+
wsMessages = win.wsMessages;
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
wsMessages = undefined;
|
|
556
|
+
}
|
|
557
|
+
// wsMessages gone → the page navigated away, nothing more to wait for
|
|
558
|
+
if (!wsMessages)
|
|
559
|
+
return { type: 'navigated' };
|
|
560
|
+
const effectiveTimeout = !options?.timeout && domLoadingIndicatorEverDetected ? 1200000 : config.timeout;
|
|
561
|
+
const timedOut = Date.now() - startTime > effectiveTimeout;
|
|
562
|
+
// A dialog we auto-dismiss needs queued cy commands — hand back to the follow-up, unless
|
|
563
|
+
// we're already over budget (fall through so handleTimeout() gets to dump its diagnostics).
|
|
564
|
+
// Cross-origin redirects (e.g. Okta) can flip win.document's origin between ticks, so this
|
|
565
|
+
// read throws — treat that the same as the wsMessages check above: page navigated away.
|
|
566
|
+
try {
|
|
567
|
+
if (!timedOut && dialogNeedsHandling(win))
|
|
568
|
+
return { type: 'dialog' };
|
|
569
|
+
if (!checkDomReadySync(win.document))
|
|
570
|
+
return timedOut ? { type: 'timeout', timeout: effectiveTimeout } : null;
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
return state.navigatingAway ? { type: 'resolved', reason: 'navigation' } : { type: 'navigated' };
|
|
557
574
|
}
|
|
558
|
-
|
|
559
|
-
|
|
575
|
+
if (!checkWsResolvedSync(win))
|
|
576
|
+
return timedOut ? { type: 'timeout', timeout: effectiveTimeout } : null;
|
|
577
|
+
// Resolved — enforce the quiet period before committing.
|
|
578
|
+
if (state.navigatingAway)
|
|
579
|
+
return { type: 'resolved', reason: 'navigation' };
|
|
560
580
|
const isInsideExternalPopup = !state.popupOpenedByThisAction && currentWin?.wsPopupFormOpen === true;
|
|
561
581
|
let quietPeriodMs;
|
|
562
582
|
if (isInsideExternalPopup || expectSearchPopup) {
|
|
@@ -568,69 +588,54 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
|
|
|
568
588
|
else {
|
|
569
589
|
quietPeriodMs = config.quietPeriodMs ?? QUIET_PERIOD_DEFAULT_MS;
|
|
570
590
|
}
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const remaining = quietPeriodMs - quietTimeSoFar;
|
|
575
|
-
if (remaining > 0) {
|
|
576
|
-
return cy.wait(remaining, { log: false }).then(() => checkAndWait());
|
|
577
|
-
}
|
|
591
|
+
// Keep ticking until the state has stayed resolved for the full quiet period since last WS activity
|
|
592
|
+
if (quietPeriodMs - (Date.now() - state.lastWsActivityTs) > 0)
|
|
593
|
+
return null;
|
|
578
594
|
state.extendedQuietPeriod = false;
|
|
579
|
-
|
|
580
|
-
return clearWebSocketMessages();
|
|
595
|
+
return { type: 'resolved' };
|
|
581
596
|
}
|
|
582
|
-
/**
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
597
|
+
/** Acts on a poll outcome, running any queued cy work, then finishes or re-enters the poll. */
|
|
598
|
+
function dispatch(outcome) {
|
|
599
|
+
switch (outcome.type) {
|
|
600
|
+
case 'navigated':
|
|
601
|
+
debugLog('🟢 Page navigated away — resolving loader');
|
|
602
|
+
log.set({ displayName: '🟢 WSLoader', message: 'Resolved' }).end();
|
|
603
|
+
return cy.wrap(undefined, { log: false });
|
|
604
|
+
case 'dialog':
|
|
605
|
+
return checkForDialogs().then((dialogType) => {
|
|
606
|
+
if (dialogType === 'locked') {
|
|
607
|
+
debugLog('🔓 Resolving loader due to locked record detection');
|
|
608
|
+
log.set({ displayName: '🟡 WSLoader', message: 'Resolved (Locked Record)' }).end();
|
|
609
|
+
return clearWebSocketMessages();
|
|
610
|
+
}
|
|
611
|
+
// Info/No-price OK-click enqueued (or the dialog vanished) — re-enter the flat poll
|
|
612
|
+
return runPoll();
|
|
613
|
+
});
|
|
614
|
+
case 'resolved':
|
|
615
|
+
log.set({ displayName: '🟢 WSLoader', message: outcome.reason === 'navigation' ? 'Resolved (navigation)' : 'Resolved' }).end();
|
|
616
|
+
return clearWebSocketMessages();
|
|
617
|
+
case 'timeout':
|
|
618
|
+
return handleTimeout(outcome.timeout);
|
|
619
|
+
case 'error':
|
|
620
|
+
throw outcome.error;
|
|
621
|
+
}
|
|
592
622
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return cy.wrap(true, { log: false });
|
|
607
|
-
}
|
|
608
|
-
processMessages(wsMessages);
|
|
609
|
-
const isResolved = isStateResolved();
|
|
610
|
-
let wsPopupFormOpen;
|
|
611
|
-
try {
|
|
612
|
-
wsPopupFormOpen = win.wsPopupFormOpen ?? false;
|
|
613
|
-
}
|
|
614
|
-
catch {
|
|
615
|
-
wsPopupFormOpen = false;
|
|
616
|
-
}
|
|
617
|
-
if (!isResolved && !state.popupOpenedByThisAction && wsPopupFormOpen) {
|
|
618
|
-
debugLog('🪟 Inside popup from previous action — using extended quiet period');
|
|
619
|
-
}
|
|
620
|
-
updateLog(isResolved, startTime);
|
|
621
|
-
return cy.wrap(isResolved, { log: false });
|
|
622
|
-
});
|
|
623
|
+
// ── Synchronous per-tick checks (no queued cy commands) ──────────────────────────────────
|
|
624
|
+
/** True when a dialog is present that the loader auto-dismisses (the dismiss needs queued cy commands). */
|
|
625
|
+
function dialogNeedsHandling(win) {
|
|
626
|
+
const dialog = findDialog(win.document);
|
|
627
|
+
if (!dialog.exists)
|
|
628
|
+
return false;
|
|
629
|
+
if (dialog.isLocked)
|
|
630
|
+
return !lockedRecordDialogDetected;
|
|
631
|
+
if (dialog.isNoPrice)
|
|
632
|
+
return true;
|
|
633
|
+
if (dialog.isInfo)
|
|
634
|
+
return !!(config.enableAutoInfoDialog ?? wsDialogHandlerEnabled);
|
|
635
|
+
return false;
|
|
623
636
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// DOM loading indicator check
|
|
627
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
628
|
-
/**
|
|
629
|
-
* Returns false (not ready) while a `<loading-indicator>` element is present in the DOM.
|
|
630
|
-
* Updates a live Cypress log entry showing how long we have been waiting.
|
|
631
|
-
*/
|
|
632
|
-
function checkDomLoadingIndicator() {
|
|
633
|
-
return cy.document({ log: false }).then((doc) => {
|
|
637
|
+
/** Synchronous form of checkDomLoadingIndicator — returns false while a loading-indicator is in the DOM. */
|
|
638
|
+
function checkDomReadySync(doc) {
|
|
634
639
|
const hasLoadingIndicator = Cypress.$(doc.body).find('loading-indicator').length > 0;
|
|
635
640
|
if (hasLoadingIndicator) {
|
|
636
641
|
if (state.domLoadingIndicatorStart === null) {
|
|
@@ -656,8 +661,37 @@ Cypress.Commands.add('WS_loadingCheck', (options) => {
|
|
|
656
661
|
state.domLoadingIndicatorStart = null;
|
|
657
662
|
domLoadingIndicatorLog = null;
|
|
658
663
|
}
|
|
659
|
-
return
|
|
660
|
-
}
|
|
664
|
+
return !hasLoadingIndicator;
|
|
665
|
+
}
|
|
666
|
+
/** Synchronous form of checkWsResolution — processes new frames and returns isStateResolved(). */
|
|
667
|
+
function checkWsResolvedSync(win) {
|
|
668
|
+
currentWin = win;
|
|
669
|
+
let wsMessages;
|
|
670
|
+
try {
|
|
671
|
+
wsMessages = win.wsMessages;
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
wsMessages = undefined;
|
|
675
|
+
}
|
|
676
|
+
if (!wsMessages) {
|
|
677
|
+
debugLog('🟢 No wsMessages found, resolving');
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
processMessages(wsMessages);
|
|
681
|
+
const isResolved = isStateResolved();
|
|
682
|
+
let wsPopupFormOpen;
|
|
683
|
+
try {
|
|
684
|
+
wsPopupFormOpen = win.wsPopupFormOpen ?? false;
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
wsPopupFormOpen = false;
|
|
688
|
+
}
|
|
689
|
+
if (!isResolved && !state.popupOpenedByThisAction && wsPopupFormOpen) {
|
|
690
|
+
debugLog('🪟 Inside popup from previous action — using extended quiet period');
|
|
691
|
+
}
|
|
692
|
+
updateLog(isResolved, startTime);
|
|
693
|
+
return isResolved;
|
|
694
|
+
}
|
|
661
695
|
}
|
|
662
696
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
663
697
|
// Message processing
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@globisnv/cypress-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Core building blocks for Globis Cypress E2E tests",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": "^22.0.0 || ^24.0.0 || >=26.0.0"
|
|
9
|
+
},
|
|
7
10
|
"repository": {
|
|
8
11
|
"type": "git",
|
|
9
12
|
"url": "git+https://github.com/globisnv/globis-cypress-core.git"
|
|
@@ -43,8 +46,8 @@
|
|
|
43
46
|
"typescript": "tsc --noEmit --skipLibCheck"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"cypress": "^
|
|
47
|
-
"cypress-real-events": "^1.15.
|
|
49
|
+
"cypress": "^16.0.0",
|
|
50
|
+
"cypress-real-events": "^1.15.1"
|
|
48
51
|
},
|
|
49
52
|
"dependencies": {
|
|
50
53
|
"@aws-crypto/sha256-js": "^5.2.0",
|
|
@@ -56,11 +59,11 @@
|
|
|
56
59
|
"devDependencies": {
|
|
57
60
|
"@biomejs/biome": "^2.5.7",
|
|
58
61
|
"@types/node": "^26.1.2",
|
|
59
|
-
"cypress": "^
|
|
60
|
-
"cypress-real-events": "^1.15.
|
|
62
|
+
"cypress": "^16.1.0",
|
|
63
|
+
"cypress-real-events": "^1.15.1",
|
|
61
64
|
"typescript": "^7.0.2"
|
|
62
65
|
},
|
|
63
66
|
"allowScripts": {
|
|
64
|
-
"cypress@
|
|
67
|
+
"cypress@16.1.0": true
|
|
65
68
|
}
|
|
66
69
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Biome formats every file with LF (formatter.lineEnding in biome.json), so check
|
|
2
|
+
# them out with LF on every platform. Without this, core.autocrlf=true rewrites
|
|
3
|
+
# them to CRLF on checkout and each Biome run leaves the whole repo flagged as
|
|
4
|
+
# modified with an empty diff.
|
|
5
|
+
* text=auto eol=lf
|
|
6
|
+
|
|
7
|
+
# Binary assets - never convert or diff their bytes
|
|
8
|
+
*.xlsx binary
|
|
9
|
+
*.jasper binary
|
|
10
|
+
*.pdf binary
|
|
11
|
+
*.png binary
|
|
12
|
+
*.jpg binary
|
package/template/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__PACKAGE_NAME__",
|
|
3
|
+
"engines": {
|
|
4
|
+
"node": "^22.0.0 || ^24.0.0 || >=26.0.0"
|
|
5
|
+
},
|
|
3
6
|
"dependencies": {
|
|
4
7
|
"@globisnv/cypress-core": "^__CORE_VERSION__",
|
|
5
|
-
"cypress-real-events": "^1.15.
|
|
8
|
+
"cypress-real-events": "^1.15.1",
|
|
6
9
|
"typescript": "^7.0.2"
|
|
7
10
|
},
|
|
8
11
|
"devDependencies": {
|
|
9
12
|
"@biomejs/biome": "^2.5.7",
|
|
10
13
|
"@types/node": "^26.1.2",
|
|
11
|
-
"cypress": "^
|
|
14
|
+
"cypress": "^16.0.0"
|
|
12
15
|
},
|
|
13
16
|
"scripts": {
|
|
14
17
|
"lint": "biome check . && node node_modules/@globisnv/cypress-core/scripts/check-unnecessary-waiting.mjs",
|