diff --git a/api/src/client/package-lock.json b/api/src/client/package-lock.json index 8f8282658..7d2596bad 100644 --- a/api/src/client/package-lock.json +++ b/api/src/client/package-lock.json @@ -22,7 +22,7 @@ "html-webpack-plugin": "^5.5.1", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", - "jsf.js_next_gen": "4.0.3-beta.5", + "jsf.js_next_gen": "4.0.4-beta.4", "mocha": "^10.2.0", "npm-check-updates": "^16.10.8", "nyc": "^15.1.0", @@ -34,7 +34,7 @@ "ts-node": "^10.9.1", "typedoc": "^0.24.4", "typescript": "^5.0.4", - "webpack": "^5.80.0", + "webpack": "^5.94.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.13.3" } @@ -2704,9 +2704,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -3437,9 +3437,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -3461,7 +3461,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -3476,6 +3476,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/array-flatten": { @@ -3509,9 +3513,9 @@ "dev": true }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, "node_modules/fast-deep-equal": { @@ -4435,9 +4439,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", @@ -5170,9 +5174,9 @@ } }, "node_modules/jsf.js_next_gen": { - "version": "4.0.3-beta.5", - "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.3-beta.5.tgz", - "integrity": "sha512-fMclWnPsObUpxr+I3YYO1fCqPjRlIAzAXjK8kOw62WSXiKGbPRsP8Q9pXoBym4vrLFU1R0/1bC5hry/rTBWpTw==", + "version": "4.0.4-beta.4", + "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.4-beta.4.tgz", + "integrity": "sha512-7Ey3/eSesFl9DLbyTBPzKWz8QjUJ7BGSE+mommstWfcE0WHOqADFXprOM8lTamYf7xkOQEVMF87Qx3qwI1038w==", "dev": true, "dependencies": { "mona-dish": "0.28.12" @@ -12363,9 +12367,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -12890,9 +12894,9 @@ } }, "express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "requires": { "accepts": "~1.3.8", @@ -12914,7 +12918,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -12956,9 +12960,9 @@ "dev": true }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true } } @@ -13649,9 +13653,9 @@ } }, "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "requires": { "@types/http-proxy": "^1.17.8", @@ -14187,9 +14191,9 @@ "dev": true }, "jsf.js_next_gen": { - "version": "4.0.3-beta.5", - "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.3-beta.5.tgz", - "integrity": "sha512-fMclWnPsObUpxr+I3YYO1fCqPjRlIAzAXjK8kOw62WSXiKGbPRsP8Q9pXoBym4vrLFU1R0/1bC5hry/rTBWpTw==", + "version": "4.0.4-beta.4", + "resolved": "https://registry.npmjs.org/jsf.js_next_gen/-/jsf.js_next_gen-4.0.4-beta.4.tgz", + "integrity": "sha512-7Ey3/eSesFl9DLbyTBPzKWz8QjUJ7BGSE+mommstWfcE0WHOqADFXprOM8lTamYf7xkOQEVMF87Qx3qwI1038w==", "dev": true, "requires": { "mona-dish": "0.28.12" diff --git a/api/src/client/package.json b/api/src/client/package.json index 6a47a10c0..4605a2e70 100644 --- a/api/src/client/package.json +++ b/api/src/client/package.json @@ -24,7 +24,7 @@ "html-webpack-plugin": "^5.5.1", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", - "jsf.js_next_gen": "4.0.3-beta.5", + "jsf.js_next_gen": "4.0.4-beta.4", "mocha": "^10.2.0", "npm-check-updates": "^16.10.8", "nyc": "^15.1.0", diff --git a/api/src/client/typescript/faces/impl/AjaxImpl.ts b/api/src/client/typescript/faces/impl/AjaxImpl.ts index 640f5f2a9..61e10a85d 100644 --- a/api/src/client/typescript/faces/impl/AjaxImpl.ts +++ b/api/src/client/typescript/faces/impl/AjaxImpl.ts @@ -55,7 +55,12 @@ import { NAMING_CONTAINER_ID, CTX_PARAM_PPS, MYFACES_OPTION_PPS, - $nsp + $nsp, + CTX_PARAM_UPLOAD_ON_PROGRESS, + CTX_PARAM_UPLOAD_PREINIT, + CTX_PARAM_UPLOAD_LOADSTART, + CTX_PARAM_UPLOAD_LOADEND, + CTX_PARAM_UPLOAD_LOAD, CTX_PARAM_UPLOAD_ERROR, CTX_PARAM_UPLOAD_ABORT, CTX_PARAM_UPLOAD_TIMEOUT } from "./core/Const"; import { resolveDefaults, @@ -338,6 +343,19 @@ export module Implementation { // pass through options are stored under _mfInternal in the context internalCtx.assign(CTX_PARAM_SRC_FRM_ID).value = formId; + /** + * special myfaces only internal parameter for onProgress until we have an official api + * that way we can track the progress of a xhr request (useful for file uploads) + */ + internalCtx.assign(CTX_PARAM_UPLOAD_PREINIT).value = options.value?.myfaces?.upload?.preinit; + internalCtx.assign(CTX_PARAM_UPLOAD_LOADSTART).value = options.value?.myfaces?.upload?.loadstart; + internalCtx.assign(CTX_PARAM_UPLOAD_ON_PROGRESS).value = options.value?.myfaces?.upload?.progress; + internalCtx.assign(CTX_PARAM_UPLOAD_LOADEND).value = options.value?.myfaces?.upload?.loadend; + internalCtx.assign(CTX_PARAM_UPLOAD_LOAD).value = options.value?.myfaces?.upload?.load; + internalCtx.assign(CTX_PARAM_UPLOAD_ERROR).value = options.value?.myfaces?.upload?.error; + internalCtx.assign(CTX_PARAM_UPLOAD_ABORT).value = options.value?.myfaces?.upload?.abort; + internalCtx.assign(CTX_PARAM_UPLOAD_TIMEOUT).value = options.value?.myfaces?.upload?.timeout; + // mojarra compatibility, mojarra is sending the form id as well // this is not documented behavior but can be determined by running // mojarra under blackbox conditions. @@ -725,7 +743,7 @@ export module Implementation { // and no prepend (aka tobago testcase "must handle ':' in IDs properly", scenario 3, // in this case we return the component id, and be happy // we can roll a dom check here - return (!!document.getElementById(finalIdentifier)) ? finalIdentifier : componentIdToTransform; + return DQ.byId(finalIdentifier).isPresent() ? finalIdentifier : componentIdToTransform; }; // in this case we do not use lazy stream because it won´t bring any code reduction diff --git a/api/src/client/typescript/faces/impl/core/Const.ts b/api/src/client/typescript/faces/impl/core/Const.ts index f5106c189..5768dd4c9 100644 --- a/api/src/client/typescript/faces/impl/core/Const.ts +++ b/api/src/client/typescript/faces/impl/core/Const.ts @@ -106,6 +106,14 @@ export const CTX_OPTIONS_EXECUTE = "execute"; export const CTX_PARAM_MF_INTERNAL = "myfaces.internal"; export const CTX_PARAM_SRC_FRM_ID = "myfaces.source.formId"; +export const CTX_PARAM_UPLOAD_ON_PROGRESS = "myfaces.upload.progress"; +export const CTX_PARAM_UPLOAD_PREINIT = "myfaces.upload.preinit"; +export const CTX_PARAM_UPLOAD_LOADSTART = "myfaces.upload.loadstart"; +export const CTX_PARAM_UPLOAD_LOADEND = "myfaces.upload.loadend"; +export const CTX_PARAM_UPLOAD_LOAD = "myfaces.upload.load"; +export const CTX_PARAM_UPLOAD_ERROR = "myfaces.upload.error"; +export const CTX_PARAM_UPLOAD_ABORT = "myfaces.upload.abort"; +export const CTX_PARAM_UPLOAD_TIMEOUT = "myfaces.upload.timeout"; export const CTX_PARAM_SRC_CTL_ID = "myfaces.source.controlId"; export const CTX_PARAM_REQ_PASS_THR = "myfaces.request.passThrough"; export const CTX_PARAM_PPS = "myfaces.request.pps"; diff --git a/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts b/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts index 6881490da..3543da3c3 100644 --- a/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts +++ b/api/src/client/typescript/faces/impl/util/ExtDomQuery.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import {Config, IValueHolder, Optional, DomQuery, DQ, Es2019Array, ValueEmbedder} from "mona-dish"; -import {$nsp, P_WINDOW_ID} from "../core/Const"; +import {$faces, $nsp, P_WINDOW_ID} from "../core/Const"; /** @@ -234,8 +234,13 @@ export class ExtDomQuery extends DQ { * @param deep whether the search should go into embedded shadow dom elements * @return a DomQuery containing the found elements */ - static byId(selector: string | DomQuery | Element, deep = false): ExtDomQuery { + static byId(selector: DomQuery | Element | string, deep = false): ExtDomQuery { const ret = DomQuery.byId(selector, deep); + if($faces().getProjectStage().toLowerCase() == "development" && + window?.console && ret.isAbsent() && selector) { + let identifier = (selector)?.id?.value ?? (selector)?.id ?? selector.toString(); + console.error("Element " + identifier + "not found"); + } return new ExtDomQuery(ret); } diff --git a/api/src/client/typescript/faces/impl/xhrCore/ErrorData.ts b/api/src/client/typescript/faces/impl/xhrCore/ErrorData.ts index cb14bd92f..f107842eb 100644 --- a/api/src/client/typescript/faces/impl/xhrCore/ErrorData.ts +++ b/api/src/client/typescript/faces/impl/xhrCore/ErrorData.ts @@ -23,7 +23,7 @@ import { STATUS, UNKNOWN } from "../core/Const"; -import {Config, Optional, XMLQuery} from "mona-dish"; +import {Config, DQ, Optional, XMLQuery} from "mona-dish"; import {EventData} from "./EventData"; import {ExtLang} from "../util/Lang"; @@ -49,7 +49,7 @@ export enum ErrorType { export class ErrorData extends EventData implements IErrorData { type: string = "error"; - source: string; + source: string | Element; errorName: string; errorMessage: string; @@ -64,9 +64,12 @@ export class ErrorData extends EventData implements IErrorData { serverErrorMessage: string; description: string; - constructor(source: string, errorName: string, errorMessage: string, responseText: string = null, responseXML: Document = null, responseCode: number = -1, statusOverride: string = null, type = ErrorType.CLIENT_ERROR) { + constructor(source: string | Element, errorName: string, errorMessage: string, responseText: string = null, responseXML: Document = null, responseCode: number = -1, statusOverride: string = null, type = ErrorType.CLIENT_ERROR) { super(); - this.source = source; + + ///MYFACES-4676 error payload expects an element if possible + //this code remaps the string in an element and if not existing just passes as is what comes in + this.source = DQ.byId(source).value.orElse(source).value; this.type = ERROR; this.errorName = errorName; diff --git a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts index 24efe5199..1efb3cac6 100644 --- a/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts +++ b/api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts @@ -48,8 +48,19 @@ import { CTX_PARAM_SRC_FRM_ID, CTX_PARAM_SRC_CTL_ID, CTX_PARAM_PPS, - EMPTY_RESPONSE, HTTP_ERROR, - EMPTY_STR, $nsp, P_BEHAVIOR_EVENT + EMPTY_RESPONSE, + HTTP_ERROR, + EMPTY_STR, + $nsp, + P_BEHAVIOR_EVENT, + CTX_PARAM_UPLOAD_ON_PROGRESS, + CTX_PARAM_UPLOAD_LOAD, + CTX_PARAM_UPLOAD_LOADSTART, + CTX_PARAM_UPLOAD_LOADEND, + CTX_PARAM_UPLOAD_ABORT, + CTX_PARAM_UPLOAD_TIMEOUT, + CTX_PARAM_UPLOAD_ERROR, + CTX_PARAM_UPLOAD_PREINIT } from "../core/Const"; import { resolveFinalUrl, @@ -117,6 +128,7 @@ export class XhrRequest extends AsyncRunnable { let xhrObject = this.xhrObject; let sourceForm = DQ.byId(this.internalContext.getIf(CTX_PARAM_SRC_FRM_ID).value) + let executesArr = () => { return this.requestContext.getIf(CTX_PARAM_REQ_PASS_THR, P_EXECUTE).get(IDENT_NONE).value.split(/\s+/gi); }; @@ -239,6 +251,36 @@ export class XhrRequest extends AsyncRunnable { xhrObject.onloadend = () => { this.onResponseProcessed(this.xhrObject, resolve); }; + + if(xhrObject?.upload) { + //this is an extension so that we can send the upload object of the current + //request before any operation + this.internalContext.getIf(CTX_PARAM_UPLOAD_PREINIT).value?.(xhrObject.upload); + //now we hook in the upload events + xhrObject.upload.addEventListener("progress", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_ON_PROGRESS).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("load", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_LOAD).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("loadstart", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_LOADSTART).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("loadend", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_LOADEND).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("abort", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_ABORT).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("timeout", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_TIMEOUT).value?.(xhrObject.upload, event); + }); + xhrObject.upload.addEventListener("error", (event: ProgressEvent) => { + this.internalContext.getIf(CTX_PARAM_UPLOAD_ERROR).value?.(xhrObject.upload, event); + }); + + } + xhrObject.onerror = (errorData: any) => { // Safari in rare cases triggers an error when cancelling a request internally, or when // in this case we simply ignore the request and clear up the queue, because diff --git a/api/src/client/typescript/faces/test/impl/util/ExtDomQueryTest.spec.ts b/api/src/client/typescript/faces/test/impl/util/ExtDomQueryTest.spec.ts new file mode 100644 index 000000000..9976daff4 --- /dev/null +++ b/api/src/client/typescript/faces/test/impl/util/ExtDomQueryTest.spec.ts @@ -0,0 +1,68 @@ +/*! Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {afterEach, describe, it} from 'mocha'; +import {expect} from 'chai'; + +import {ExtDomQuery} from "../../../impl/util/ExtDomQuery"; +import {StandardInits} from "../../frameworkBase/_ext/shared/StandardInits"; +import defaultMyFaces = StandardInits.defaultMyFaces; +import Sinon from "sinon"; + +declare var faces: any; +let oldProjectStage = null; + +/** + * Tests for our extended DomQuery functionality + */ +describe('ExtDomQuery test suite', () => { + beforeEach(async () => { + Sinon.reset(); + //we test ES2019 with it and whether we have missed + //a map somewhere + return await defaultMyFaces().then(() => { + oldProjectStage = faces.getProjectStage; + faces.getProjectStage = () => "Development"; + }); + }); + + afterEach(() => { + faces.getProjectStage = oldProjectStage; + }); + + it('ExtDomQuery.By id must log a console error with ProjectStage == Development and non existing id', (done) => { + const spy = Sinon.spy(window.console, "error"); + try { + ExtDomQuery.byId("no_existent_element"); + expect(spy.calledOnce).to.be.true; + } finally { + spy.restore(); + } + done(); + }); + + it('ExtDomQuery.By id must not log a console error with ProjectStage == Production and non existing id', (done) => { + faces.getProjectStage = () => "Production"; + const spy = Sinon.spy(window.console, "error"); + try { + ExtDomQuery.byId("no_existent_element"); + expect(spy.notCalled).to.be.true; + } finally { + spy.restore(); + } + done(); + }); +}); diff --git a/api/src/client/typescript/faces/test/myfaces/OamSubmit.spec.ts b/api/src/client/typescript/faces/test/myfaces/OamSubmit.spec.ts index f34f68d66..8f6f654dc 100644 --- a/api/src/client/typescript/faces/test/myfaces/OamSubmit.spec.ts +++ b/api/src/client/typescript/faces/test/myfaces/OamSubmit.spec.ts @@ -24,7 +24,7 @@ import setHiddenInput = oam.setHiddenInput; import {DomQuery} from "mona-dish"; import clearHiddenInput = oam.clearHiddenInput; import submitForm = oam.submitForm; -import Sinon from "sinon"; +import Sinon, {spy} from "sinon"; declare var myfaces: any; @@ -38,28 +38,28 @@ describe('Tests on the xhr core when it starts to call the request', function () return defaultMyFaces(); }) - it('namespace must exist', function() { + it('namespace must exist', function () { expect(!!myfaces?.oam).to.eq(true); expect(!!myfaces?.oam?.setHiddenInput).to.eq(true); expect(!!myfaces?.oam?.clearHiddenInput).to.eq(true); expect(!!myfaces?.oam?.submitForm).to.eq(true); }); - it('hidden input setting must work', function() { + it('hidden input setting must work', function () { let FORM_ID = "blarg"; setHiddenInput(FORM_ID, "new_hidden", "hiddenvalue"); expect(DomQuery.byId(FORM_ID).querySelectorAll("input[name='new_hidden']").isPresent()); expect(DomQuery.byId(FORM_ID).querySelectorAll("input[name='new_hidden']").inputValue.value).to.eq("hiddenvalue"); }) - it('resetting the hidden input must work', function() { + it('resetting the hidden input must work', function () { let FORM_ID = "blarg"; setHiddenInput(FORM_ID, "new_hidden", "hiddenvalue"); clearHiddenInput(FORM_ID, "new_hidden"); expect(DomQuery.byId(FORM_ID).querySelectorAll("input[name='new_hidden']").isAbsent()).to.eq(true); }) - it('submit form must work', function() { + it('submit form must work', function () { let FORM_ID = "blarg"; let form = DomQuery.byId(FORM_ID); const submit_spy = Sinon.spy(() => { @@ -90,7 +90,7 @@ describe('Tests on the xhr core when it starts to call the request', function () }) - it('onsubmit form must work', function() { + it('onsubmit form must work', function () { let FORM_ID = "blarg"; let form = DomQuery.byId(FORM_ID); const onsumbit = () => { @@ -107,7 +107,9 @@ describe('Tests on the xhr core when it starts to call the request', function () return false; } const os_spy = Sinon.spy(onsumbit); - const submit_spy = Sinon.spy(() => {}); + const submit_spy = Sinon.spy(() => { + }); + (form.value.value as any).onsubmit = os_spy; (form.value.value as any).submit = submit_spy; @@ -125,7 +127,7 @@ describe('Tests on the xhr core when it starts to call the request', function () expect(form.querySelectorAll('input[name=\'booga1\']').isAbsent()).to.eq(true); expect(form.querySelectorAll('input[name=\'booga2\']').isAbsent()).to.eq(true); expect(form.querySelectorAll(`input[name='${FORM_ID}:_idcl']`).isAbsent()).to.eq(true); - // expect(submit_spy.called).to.eq(true); + }) // further tests will follow if needed, for now the namespace must be restored diff --git a/api/src/client/typescript/faces/test/xhrCore/ErrorChainTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/ErrorChainTest.spec.ts index 500d86de3..3e0908ef0 100644 --- a/api/src/client/typescript/faces/test/xhrCore/ErrorChainTest.spec.ts +++ b/api/src/client/typescript/faces/test/xhrCore/ErrorChainTest.spec.ts @@ -24,6 +24,7 @@ import {expect} from "chai"; import {Implementation} from "../../impl/AjaxImpl"; import errorChainPage = StandardInits.errorChainPage; import {DQ} from "mona-dish"; +import {ErrorData} from "../../impl/xhrCore/ErrorData"; /** * Tests for error recover if an error is triggered mid chain @@ -131,6 +132,16 @@ describe('Tests of the various aspects of the response protocol functionality', expect(DQ.byId("errorCalled").innerHTML).to.eq("1"); expect(DQ.byId("form1:out1").innerHTML).to.eq("5"); done(); + }); + + it('must have correct source element within the error Data Object', () => { + const errorData = new ErrorData("form1:button1", "errorName", "errorMessage"); + expect((errorData.source).id).to.eq("form1:button1"); + }) + + it('should have correct source id string within the error Data Object if element not existing', () => { + const errorData = new ErrorData("form1:button1:booga", "errorName", "errorMessage"); + expect(errorData.source).to.eq("form1:button1:booga"); }) }); diff --git a/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts b/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts index 269f15af1..d17d390b7 100644 --- a/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts +++ b/api/src/client/typescript/faces/test/xhrCore/ResponseTest.spec.ts @@ -740,7 +740,7 @@ describe('Tests of the various aspects of the response protocol functionality', faces.ajax.addOnError((error) => { expect(error.errorName).to.eq("jakarta.faces.application.ViewExpiredException"); expect(error.errorMessage).to.eq("View \"/testhmtl.xhtml\" could not be restored."); - expect(error.source).to.eq("form1x:button"); + expect(error.source.id).to.eq("form1x:button"); errorCalled++; }); diff --git a/api/src/client/typescript/faces/test/xhrCore/XhrRequestProgress.spec.ts b/api/src/client/typescript/faces/test/xhrCore/XhrRequestProgress.spec.ts new file mode 100644 index 000000000..7c99b5281 --- /dev/null +++ b/api/src/client/typescript/faces/test/xhrCore/XhrRequestProgress.spec.ts @@ -0,0 +1,95 @@ +import {StandardInits} from "../frameworkBase/_ext/shared/StandardInits"; +import * as sinon from "sinon"; +import {Implementation} from "../../impl/AjaxImpl"; +import {expect} from "chai"; +import protocolPage = StandardInits.protocolPage; + +const jsdom = require("jsdom"); +const {JSDOM} = jsdom; + + +describe("Should trigger the progress on xhr request", function () { + beforeEach(async function () { + let waitForResult = protocolPage(); + + //build up the test fixture + return waitForResult.then((close) => { + //we generate an xhr mock class replacement + this.xhr = sinon.useFakeXMLHttpRequest(); + this.requests = []; + + //we store the requests to have access to them later + this.xhr.onCreate = (xhr) => { + this.requests.push(xhr); + }; + //we anchchor the mock into the fake dom + (global).XMLHttpRequest = this.xhr; + window.XMLHttpRequest = this.xhr; + + //general cleanup of overloaded resources + this.closeIt = () => { + (global).XMLHttpRequest = window.XMLHttpRequest = this.xhr.restore(); + Implementation.reset(); + close(); + }; + }); + }); + afterEach(function () { + this.closeIt(); + }); + + it("must trigger progress on xhr request", function() { + let caughtProgressEvents = []; + var preinitTriggered = false; + var loadstartTriggered = false; + var loadTriggered = false; + var loadendTriggered = false; + var timeoutTriggered = false; + var abortTriggered = false; + var errorTriggered = false; + faces.ajax.request(document.getElementById("cmd_eval"), null, + { + render: '@form', + execute: '@form', + myfaces: { + upload: { + progress: (upload: XMLHttpRequestUpload, event: ProgressEvent) => { + caughtProgressEvents.push(event); + }, + preinit: (upload: XMLHttpRequestUpload) => preinitTriggered = true, + loadstart: (upload: XMLHttpRequestUpload, event: ProgressEvent) => loadstartTriggered = true, + load: (upload: XMLHttpRequestUpload, event: ProgressEvent) => loadTriggered = true, + loadend: (upload: XMLHttpRequestUpload, event: ProgressEvent) => loadendTriggered = true, + error: (upload: XMLHttpRequestUpload, event: ProgressEvent) => errorTriggered = true, + abort: (upload: XMLHttpRequestUpload, event: ProgressEvent) => abortTriggered = true, + timeout: (upload: XMLHttpRequestUpload, event: ProgressEvent) => timeoutTriggered = true, + + } + } + }); + + let progressEvent = new ProgressEvent("progress"); + let progressEvent2 = new ProgressEvent("progress"); + let xhr = this.requests.shift(); + xhr.upload.dispatchEvent(new ProgressEvent("loadstart")); + xhr.upload.dispatchEvent(new ProgressEvent("load")); + xhr.upload.dispatchEvent(progressEvent); + xhr.upload.dispatchEvent(progressEvent2); + xhr.upload.dispatchEvent(new ProgressEvent("loadend")); + xhr.upload.dispatchEvent(new ProgressEvent("error")); + xhr.upload.dispatchEvent(new ProgressEvent("abort")); + xhr.upload.dispatchEvent(new ProgressEvent("timeout")); + + expect(caughtProgressEvents.length).to.eq(2); + expect(caughtProgressEvents[0] === progressEvent).to.eq(true); + expect(caughtProgressEvents[1] === progressEvent2).to.eq(true); + + expect(preinitTriggered).to.eq(true); + expect(loadstartTriggered).to.eq(true); + expect(loadTriggered).to.eq(true); + expect(loadendTriggered).to.eq(true); + expect(errorTriggered).to.eq(true); + expect(abortTriggered).to.eq(true); + expect(timeoutTriggered).to.eq(true); + }); +}); \ No newline at end of file