diff --git a/integration_test/e2e-ui-tests/.testcaferc.json b/integration_test/e2e-ui-tests/.testcaferc.json index ee7735f..e6f06a0 100644 --- a/integration_test/e2e-ui-tests/.testcaferc.json +++ b/integration_test/e2e-ui-tests/.testcaferc.json @@ -8,5 +8,6 @@ "customCompilerModulePath": "../ts4", "configPath": "tsconfig.json" } - } + }, + "disablePageCaching": false } diff --git a/integration_test/e2e-ui-tests/package-lock.json b/integration_test/e2e-ui-tests/package-lock.json index 8441b73..ecc3da3 100644 --- a/integration_test/e2e-ui-tests/package-lock.json +++ b/integration_test/e2e-ui-tests/package-lock.json @@ -9,8 +9,11 @@ "version": "1.0.0", "dependencies": { "@js-temporal/polyfill": "^0.2.0", + "@types/async-retry": "^1.4.3", "@types/uuid": "^8.3.1", "@types/ws": "^6.0.4", + "async-retry": "^1.3.3", + "chai": "^4.3.6", "dotenv": "^10.0.0", "exceljs": "^4.3.0", "got": "^11.8.3", @@ -22,6 +25,7 @@ "uuid": "^3.4.0" }, "devDependencies": { + "@types/chai": "^4.3.1", "html-webpack-plugin": "^5.3.1" } }, @@ -1831,6 +1835,14 @@ "node": ">=10" } }, + "node_modules/@types/async-retry": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.4.3.tgz", + "integrity": "sha512-B3C9QmmNULVPL2uSJQ088eGWTNPIeUk35hca6CV8rRDJ8GXuQJP5CCVWA1ZUCrb9xYP7Js/RkLqnNNwKhe+Zsw==", + "dependencies": { + "@types/retry": "*" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -1842,33 +1854,17 @@ "@types/responselike": "*" } }, + "node_modules/@types/chai": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", + "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", + "dev": true + }, "node_modules/@types/error-stack-parser": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", "integrity": "sha1-4ByfjIXKg7YQMgxiJYsMkCat4Pc=" }, - "node_modules/@types/eslint": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", - "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "0.0.46", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", @@ -1894,13 +1890,6 @@ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true, - "peer": true - }, "node_modules/@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -1932,6 +1921,11 @@ "@types/node": "*" } }, + "node_modules/@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + }, "node_modules/@types/uuid": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", @@ -1945,194 +1939,6 @@ "@types/node": "*" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true - }, - "node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-hammerhead": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.5.0.tgz", @@ -2141,16 +1947,6 @@ "@types/estree": "0.0.46" } }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2171,33 +1967,6 @@ "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -2359,6 +2128,14 @@ "node": ">=0.12.0" } }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dependencies": { + "retry": "0.13.1" + } + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2712,14 +2489,15 @@ } }, "node_modules/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" }, @@ -2796,16 +2574,6 @@ } } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -3391,20 +3159,6 @@ "node": ">=0.10.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -3455,13 +3209,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true, - "peer": true - }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -3494,20 +3241,6 @@ "node": ">=0.8.0" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/esotope-hammerhead": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.1.tgz", @@ -3516,39 +3249,6 @@ "@types/estree": "0.0.46" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3557,16 +3257,6 @@ "node": ">=0.10.0" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/exceljs": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz", @@ -3674,13 +3364,6 @@ "node": ">=10.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -3696,13 +3379,6 @@ "node": ">=8" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -3897,13 +3573,6 @@ "node": ">=0.10.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -4525,47 +4194,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "node_modules/jest-worker": { - "version": "27.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", - "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4587,20 +4215,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true, - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, "node_modules/json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -4666,16 +4280,6 @@ "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" }, - "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.11.5" - } - }, "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -4793,6 +4397,14 @@ "node": ">=4" } }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -4886,19 +4498,6 @@ "node": ">= 0.6" } }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "peer": true, - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -5027,13 +4626,6 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", "integrity": "sha512-4ug4BsuHxiVHoRUe1ud6rUFT3WUMmjXt1W0quL0CviZQANdan7D8kqN5/maw53hmAApY/jfzMRkC57BNNs60ZQ==" }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -5425,16 +5017,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/qrcode-terminal": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.10.0.tgz", @@ -5473,16 +5055,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/read-file-relative": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/read-file-relative/-/read-file-relative-1.2.0.tgz", @@ -5691,6 +5263,14 @@ "lowercase-keys": "^2.0.0" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5776,25 +5356,6 @@ "node": ">=10" } }, - "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -5803,16 +5364,6 @@ "semver": "bin/semver" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -5883,6 +5434,7 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dependencies": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -5903,7 +5455,8 @@ "node_modules/source-map-url": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated" }, "node_modules/stackframe": { "version": "0.3.1", @@ -6075,41 +5628,6 @@ "node": ">=10" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", - "dev": true, - "peer": true, - "dependencies": { - "jest-worker": "^27.0.6", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -6638,16 +6156,9 @@ }, "node_modules/ts4": { "name": "typescript", - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==" }, "node_modules/tslib": { "version": "2.3.1", @@ -6774,16 +6285,6 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -6815,20 +6316,6 @@ "uuid": "bin/uuid" } }, - "node_modules/watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", - "dev": true, - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/webauth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/webauth/-/webauth-1.1.0.tgz", @@ -6837,71 +6324,6 @@ "node": ">= 0.10.0" } }, - "node_modules/webpack": { - "version": "5.63.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.63.0.tgz", - "integrity": "sha512-HYrw6bkj/MDmphAXvqLEvn2fVoDZsYu6O638WjK6lSNgIpjb5jl/KtOrqJyU9EC/ZV9mLUmZW5h4mASB+CVA4A==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.2.0", - "webpack-sources": "^3.2.0" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", - "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true, - "peer": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8290,6 +7712,14 @@ "defer-to-connect": "^2.0.0" } }, + "@types/async-retry": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.4.3.tgz", + "integrity": "sha512-B3C9QmmNULVPL2uSJQ088eGWTNPIeUk35hca6CV8rRDJ8GXuQJP5CCVWA1ZUCrb9xYP7Js/RkLqnNNwKhe+Zsw==", + "requires": { + "@types/retry": "*" + } + }, "@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -8301,33 +7731,17 @@ "@types/responselike": "*" } }, + "@types/chai": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", + "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", + "dev": true + }, "@types/error-stack-parser": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", "integrity": "sha1-4ByfjIXKg7YQMgxiJYsMkCat4Pc=" }, - "@types/eslint": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", - "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", - "dev": true, - "peer": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "@types/estree": { "version": "0.0.46", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", @@ -8353,13 +7767,6 @@ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true, - "peer": true - }, "@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -8391,6 +7798,11 @@ "@types/node": "*" } }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + }, "@types/uuid": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", @@ -8404,188 +7816,6 @@ "@types/node": "*" } }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true, - "peer": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true - }, - "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", - "dev": true, - "peer": true - }, "acorn-hammerhead": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.5.0.tgz", @@ -8594,14 +7824,6 @@ "@types/estree": "0.0.46" } }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peer": true, - "requires": {} - }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -8618,27 +7840,6 @@ } } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, - "requires": {} - }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -8763,6 +7964,14 @@ "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-1.1.2.tgz", "integrity": "sha1-gJXXXkiMKazuBVH+hyUhadeJz7o=" }, + "async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "requires": { + "retry": "0.13.1" + } + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -9024,14 +8233,15 @@ "integrity": "sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==" }, "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" } @@ -9081,13 +8291,6 @@ } } }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true - }, "chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -9542,17 +8745,6 @@ } } }, - "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, - "peer": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -9594,13 +8786,6 @@ "unbox-primitive": "^1.0.1" } }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true, - "peer": true - }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -9621,17 +8806,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "peer": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, "esotope-hammerhead": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.1.tgz", @@ -9640,44 +8814,11 @@ "@types/estree": "0.0.46" } }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "peer": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "peer": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "peer": true - }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true - }, "exceljs": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz", @@ -9757,13 +8898,6 @@ "@fast-csv/parse": "4.3.6" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -9776,13 +8910,6 @@ "micromatch": "^4.0.4" } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -9927,13 +9054,6 @@ } } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -10352,37 +9472,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "jest-worker": { - "version": "27.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", - "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10398,20 +9487,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true, - "peer": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -10468,13 +9543,6 @@ "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, - "peer": true - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -10585,6 +9653,14 @@ } } }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "requires": { + "get-func-name": "^2.0.0" + } + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -10656,16 +9732,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "peer": true, - "requires": { - "mime-db": "1.51.0" - } - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -10765,13 +9831,6 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", "integrity": "sha512-4ug4BsuHxiVHoRUe1ud6rUFT3WUMmjXt1W0quL0CviZQANdan7D8kqN5/maw53hmAApY/jfzMRkC57BNNs60ZQ==" }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -11055,13 +10114,6 @@ "once": "^1.3.1" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "peer": true - }, "qrcode-terminal": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.10.0.tgz", @@ -11077,16 +10129,6 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "read-file-relative": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/read-file-relative/-/read-file-relative-1.2.0.tgz", @@ -11267,6 +10309,11 @@ "lowercase-keys": "^2.0.0" } }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -11314,33 +10361,11 @@ "xmlchars": "^2.2.0" } }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "peer": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "peer": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -11563,20 +10588,6 @@ } } }, - "terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", - "dev": true, - "peer": true, - "requires": { - "jest-worker": "^27.0.6", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - } - }, "testcafe": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", @@ -12011,9 +11022,9 @@ } }, "ts4": { - "version": "npm:typescript@4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==" + "version": "npm:typescript@4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==" }, "tslib": { "version": "2.3.1", @@ -12108,16 +11119,6 @@ } } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "requires": { - "punycode": "^2.1.0" - } - }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -12144,71 +11145,11 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, - "watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", - "dev": true, - "peer": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, "webauth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/webauth/-/webauth-1.1.0.tgz", "integrity": "sha1-ZHBPa4AmmGYFvDymKZUubib90QA=" }, - "webpack": { - "version": "5.63.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.63.0.tgz", - "integrity": "sha512-HYrw6bkj/MDmphAXvqLEvn2fVoDZsYu6O638WjK6lSNgIpjb5jl/KtOrqJyU9EC/ZV9mLUmZW5h4mASB+CVA4A==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.2.0", - "webpack-sources": "^3.2.0" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true, - "peer": true - } - } - }, - "webpack-sources": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", - "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==", - "dev": true, - "peer": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/integration_test/e2e-ui-tests/package.json b/integration_test/e2e-ui-tests/package.json index 57258e9..379644d 100644 --- a/integration_test/e2e-ui-tests/package.json +++ b/integration_test/e2e-ui-tests/package.json @@ -8,12 +8,14 @@ "@js-temporal/polyfill": "^0.2.0", "@types/uuid": "^8.3.1", "@types/ws": "^6.0.4", + "@types/async-retry": "^1.4.3", "dotenv": "^10.0.0", "exceljs": "^4.3.0", "got": "^11.8.3", "mojaloop-ts": "v0.1.0-git-ca05c40b2ad8740790107b293bf3bcebbb95ef33", "mojaloop-voodoo-client": "0.7.0", "testcafe": "^1.17.1", + "async-retry": "^1.3.3", "testcafe-react-selectors": "^4.1.5", "ts4": "npm:typescript@^4.3.5", "uuid": "^3.4.0" diff --git a/integration_test/e2e-ui-tests/src/tests/SettlementsPage.test.ts b/integration_test/e2e-ui-tests/src/tests/SettlementsPage.test.ts index f883228..14cdc86 100644 --- a/integration_test/e2e-ui-tests/src/tests/SettlementsPage.test.ts +++ b/integration_test/e2e-ui-tests/src/tests/SettlementsPage.test.ts @@ -8,6 +8,7 @@ import { VoodooClient, protocol } from 'mojaloop-voodoo-client'; import { v4 as uuidv4 } from 'uuid'; import { ledger as ledgerApi, settlement as settlementApi, reporting as reportingApi, types } from 'mojaloop-ts'; import ExcelJS from 'exceljs'; +import retry from 'async-retry'; // This has really come from: // import { extractSwitchIdentifiers } from '../../../../src/App/Settlements/helpers'; @@ -46,8 +47,8 @@ fixture `Settlements Feature` }) .beforeEach(async (t) => { const accounts: protocol.AccountInitialization[] = [ - { currency: CURRENCY, initial_position: '0', ndc: 10000 }, - { currency: CURRENCY, initial_position: '0', ndc: 10000 }, + { currency: CURRENCY, initial_position: '100', ndc: 10000 }, + { currency: CURRENCY, initial_position: '100', ndc: 10000 }, ]; const participants = await t.fixtureCtx.cli.createParticipants(accounts); @@ -78,11 +79,9 @@ test.meta({ const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); - await settlementApi.closeSettlementWindow( - settlementsBasePath, - openWindows1[0].settlementWindowId, - 'Integration test', - ); + + let settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); // Run a transfer so the settlement window can be closed const transfers2: protocol.TransferMessage[] = [{ @@ -95,11 +94,9 @@ test.meta({ await cli.completeTransfers(transfers2); const openWindows2 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); await t.expect(openWindows2.length).eql(1, 'Expected only a single open window'); - await settlementApi.closeSettlementWindow( - settlementsBasePath, - openWindows2[0].settlementWindowId, - 'Integration test', - ); + + settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows2[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows2[0].settlementWindowId}`); const settlementWindowIds = [ openWindows1[0].settlementWindowId, @@ -182,7 +179,7 @@ test.meta({ await t.click(SettlementFinalizingModal.processButton); // Processing can take some time, use a high timeout - await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 60000 })); + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); const rowsAfter = await SettlementsPage.getResultRows(); const settlementRowAfter = await Promise.any(rowsAfter.map( (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), @@ -233,19 +230,1148 @@ test.meta({ const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); await t.expect(dfspNames.includes(participants[0].name)).ok() await t.expect(dfspNames.includes(participants[1].name)).ok() - await t.click(SettlementDetailModal.closeButton) + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) }); +test.meta({ + ID: '', + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('SAME balance of SENDER on the settlement finalization report should not block the settlement process', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); -test.skip.meta({ + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 110); + participantBalances.set(participants[1].name, 100); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.meta({ ID: '', - STORY: 'MMD-440', -})( - `Once I click Settlement tab in Side Menu, the page on the right should come up with - Date drop-down defaulted to Today, From and To drop-down defaulted to current date in MM/DD/YYYY HH:MM:SS format - State should be empty and Clear Filters button`, - async (t) => { - // Call Mojaloop Settlement API to get the current window details - // Check that the latest window ID that displays on the page is the same - }, -); + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('HIGHER balance of SENDER on the settlement finalization report should not block the settlement process', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 110); + participantBalances.set(participants[1].name, 110); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.meta({ + ID: '', + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('LOWER balance of SENDER on the settlement finalization report should not block the settlement process', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 110); + participantBalances.set(participants[1].name, 80); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.meta({ + ID: '', + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('SAME balance of RECEIVER on the settlement finalization report should not block the settlement process.', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 100); + participantBalances.set(participants[1].name, 90); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.meta({ + ID: '', + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('HIGHER balance of RECEIVER on the settlement finalization report should not block the settlement process', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 120); + participantBalances.set(participants[1].name, 90); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.meta({ + ID: '', + STORY: 'MMD-2209', + description: + `Same balance in Settlement Finalisation report on payer side causes Finalisation to hang`, +})('LOWER balance of RECEIVER on the settlement finalization report should not block the settlement process', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '10', + transfer_id: uuidv4(), + }]; + + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 90); + participantBalances.set(participants[1].name, 90); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.skip.meta({ + ID: '', + STORY: 'MMD-1989', + description: + `Request to Validate Minus balance in Settlement Finalization Report`, +})('Validate Minus balance in Settlement Finalization Report', async (t) => { + type Context = { cli: VoodooClient; participants: protocol.ClientParticipant[] }; + const { cli, participants } = t.fixtureCtx as Context; + // Run a transfer to ensure the settlement window can be closed + const transfers1: protocol.TransferMessage[] = [{ + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: CURRENCY, + amount: '200', + transfer_id: uuidv4(), + }]; + await cli.completeTransfers(transfers1); + const nullWindowQueryParams = { currency: null, participantId: null, state: null, fromDateTime: null, toDateTime: null }; + const openWindows1 = await cli.getSettlementWindows({ ...nullWindowQueryParams, state: "OPEN" }); + await t.expect(openWindows1.length).eql(1, 'Expected only a single open window'); + + const settlementWindowState = await closeSettlementWindowWaitAndRetry(openWindows1[0].settlementWindowId); + await t.expect(settlementWindowState).eql('CLOSED', `Expected settlementWindowState to be CLOSED settlementWindowId=${openWindows1[0].settlementWindowId}`); + + const settlementWindowIds = [ + openWindows1[0].settlementWindowId, + ]; + + const settlement = await settlementApi.createSettlement( + settlementsBasePath, + { + reason: 'Integration test', + settlementModel: 'DEFERREDNET', + settlementWindows: settlementWindowIds.map((id) => ({ id })), + }, + ); + + // Get the initiation report, "simulate" some balances returned by the settlement bank, save it + // as the finalization report. + const participantBalances = new Map(); + participantBalances.set(participants[0].name, 300); + participantBalances.set(participants[1].name, -100); + const initiationReport = + await reportingApi.getSettlementInitiationReport(reportBasePath, settlement.id); + const wb = new ExcelJS.Workbook(); + await wb.xlsx.load(initiationReport.body); + const ws = wb.getWorksheet(1); + const BALANCE_COL = 'C'; + const PARTICIPANT_INFO_COL = 'A'; + const START_OF_DATA = 7; + let firstEmptyDataRow = START_OF_DATA; + while (ws.getCell(`A${firstEmptyDataRow}`).text !== '') { + firstEmptyDataRow += 1; + } + const balanceInfo = ws.getRows(START_OF_DATA, firstEmptyDataRow - START_OF_DATA)?.map((row) => { + const participantInfo = row.getCell(PARTICIPANT_INFO_COL); + const name = extractParticipantName(participantInfo.text); + // TODO: sometimes there might be extra participants in the settlement because of other tests. + const balance = participantBalances.get(name); + return { + balance, + participantInfo, + rowNum: row.number, + row, + }; + }); + await t.expect(balanceInfo).notEql( + undefined, + 'Expect some data rows in the settlement initiation report' + ); + balanceInfo?.forEach(({ balance, row }) => { + row.getCell(BALANCE_COL).value = balance; + }); + const filename = __dirname + `/settlement-finalization-report-settlement-${settlement.id}.xlsx`; + await wb.xlsx.writeFile(filename); + + await t.click(SideMenu.settlementsButton); + + const rowsBefore = await SettlementsPage.getResultRows(); + const settlementRowBefore = await Promise.any(rowsBefore.map( + (r) => r.id.innerText.then( + id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject(), + ), + )); + await t.expect(settlementRowBefore.state.innerText).eql('Pending Settlement'); + await t.click(settlementRowBefore.finalizeButton); + + await t.setFilesToUpload(SettlementFinalizingModal.fileInput, [filename]); + if (!await SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox.checked) { + await t.click(SettlementFinalizingModal.setLiquidityAccountBalanceCheckbox); + } + if (!await SettlementFinalizingModal.increaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.increaseNdcCheckbox); + } + if (!await SettlementFinalizingModal.decreaseNdcCheckbox.checked) { + await t.click(SettlementFinalizingModal.decreaseNdcCheckbox); + } + await t.click(SettlementFinalizingModal.validateButton); + // The warning dialog will appear, dismiss it. Validation can take some time, use a high timeout. + await t.click(Selector(SettlementFinalizationWarningModal.closeButton, { timeout: 60000 })); + await t.click(SettlementFinalizingModal.processButton); + + // Processing can take some time, use a high timeout + await t.click(Selector(SettlementFinalizingModal.closeButton, { timeout: 100000 })); + const rowsAfter = await SettlementsPage.getResultRows(); + const settlementRowAfter = await Promise.any(rowsAfter.map( + (r) => r.id.innerText.then(id => Number(id) === settlement.id ? Promise.resolve(r) : Promise.reject()), + )); + + await t.expect(settlementRowAfter.state.innerText).eql('Settled'); + + async function getParticipantSettlementAccount( + p: protocol.ClientParticipant + ): Promise<[types.FspName, types.AccountWithPosition | undefined]> { + return ledgerApi.getParticipantAccounts(ledgerBasePath, p.name).then((accs) => [ + p.name, + accs.find( + (acc) => acc.ledgerAccountType === 'SETTLEMENT' && acc.currency === p.account.currency + ), + ]); + } + const [limits, accounts] = await Promise.all([ + ledgerApi.getParticipantsLimits(ledgerBasePath).then((lims) => lims + .filter((lim) => lim.currency === CURRENCY && lim.limit.type === 'NET_DEBIT_CAP') + .reduce( + (map, lim) => map.set(lim.name, lim), + new Map(), + ) + ), + Promise.all(participants.map(getParticipantSettlementAccount)).then( + (accounts) => new Map(accounts) + ), + ]); + const expectedAccountState = Object.fromEntries( + [...participantBalances.entries()].map( + ([name, bal]) => [name, { balance: -bal, limit: bal }] + ), + ); + const actualAccountState = Object.fromEntries( + [...participantBalances.keys()].map( + (name) => [name, { balance: accounts.get(name)?.value, limit: limits.get(name)?.limit.value }] + ), + ); + await t.expect(actualAccountState).eql( + expectedAccountState, + 'All participant settlement account balances and NDCs should have been set correctly' + ); + + // Open the detail modal to check if DFSP name is rendered for the settlement + await t.click(Selector(settlementRowAfter.id)) + const settlementDetailRows = await SettlementDetailModal.getWindowsRows() + const dfspNames = await Promise.all(settlementDetailRows.map((r: WindowRow) => r.dfsp.innerText)); + await t.expect(dfspNames.includes(participants[0].name)).ok() + await t.expect(dfspNames.includes(participants[1].name)).ok() + await t.click(Selector(SettlementDetailModal.closeButton, { timeout: 60000 })) +}); + +test.skip.meta({ + ID: '', + STORY: 'MMD-440', +})( + `Once I click Settlement tab in Side Menu, the page on the right should come up with + Date drop-down defaulted to Today, From and To drop-down defaulted to current date in MM/DD/YYYY HH:MM:SS format + State should be empty and Clear Filters button`, + async (t) => { + // Call Mojaloop Settlement API to get the current window details + // Check that the latest window ID that displays on the page is the same + }, +); + +async function closeSettlementWindowWaitAndRetry(settlementWindowId: number) { + await settlementApi.closeSettlementWindow( + settlementsBasePath, + settlementWindowId, + 'Integration test' + ); + const settlementWindowState = await retry( + async () => { + // if anything throws, we retry + const res = await settlementApi.getSettlementWindow( + settlementsBasePath, + settlementWindowId + ); + if ('CLOSED' !== res.state) { + throw new Error(); + } + return res.state; + }, + { + retries: 5, + maxTimeout: 5000, + } + ); + return settlementWindowState; +}