From f879f563084fca85efac424100fc448f83c913a0 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Mon, 14 Oct 2019 17:34:58 +0200 Subject: [PATCH 01/11] Progress on Solveur GUI --- .../calculators/solveur/solveur.config.json | 32 +++++++++++ src/app/calculators/solveur/solveur.en.json | 10 ++++ src/app/calculators/solveur/solveur.fr.json | 10 ++++ .../definition/concrete/form-solveur.ts | 53 +++++++++++++++++++ src/app/formulaire/fieldset.ts | 21 ++++++++ src/app/formulaire/select-field.ts | 25 ++++++++- src/app/services/formulaire.service.ts | 6 +++ src/locale/messages.en.json | 2 + src/locale/messages.fr.json | 2 + 9 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/app/calculators/solveur/solveur.config.json create mode 100644 src/app/calculators/solveur/solveur.en.json create mode 100644 src/app/calculators/solveur/solveur.fr.json create mode 100644 src/app/formulaire/definition/concrete/form-solveur.ts diff --git a/src/app/calculators/solveur/solveur.config.json b/src/app/calculators/solveur/solveur.config.json new file mode 100644 index 000000000..7e6ac3bbd --- /dev/null +++ b/src/app/calculators/solveur/solveur.config.json @@ -0,0 +1,32 @@ +[ + { + "id": "fs_target", + "type": "fieldset", + "fields": [ + { + "id": "select_target_nub", + "type": "select", + "source": "solveur_target" + }, + "Ytarget" + ] + }, + { + "id": "fs_searched", + "type": "fieldset", + "fields": [ + { + "id": "select_searched_param", + "type": "select", + "source": "solveur_searched" + }, + "Xinit" + ] + }, + { + "type": "options", + "targetNubSelectId": "select_target_nub", + "searchedParamSelectId": "select_searched_param", + "_help": "solveur.html" + } +] \ No newline at end of file diff --git a/src/app/calculators/solveur/solveur.en.json b/src/app/calculators/solveur/solveur.en.json new file mode 100644 index 000000000..cbf67bcf2 --- /dev/null +++ b/src/app/calculators/solveur/solveur.en.json @@ -0,0 +1,10 @@ +{ + "fs_target": "Target parameter characteristics", + "fs_searched": "Searched parameter characteristics", + + "Ytarget": "Value of target parameter", + "Xinit": "Initial value for searched parameter", + + "select_target_nub": "Module and parameter to calculate", + "select_searched_param": "Searched parameter" +} \ No newline at end of file diff --git a/src/app/calculators/solveur/solveur.fr.json b/src/app/calculators/solveur/solveur.fr.json new file mode 100644 index 000000000..86899fc0d --- /dev/null +++ b/src/app/calculators/solveur/solveur.fr.json @@ -0,0 +1,10 @@ +{ + "fs_target": "Caractéristiques du paramètre cible", + "fs_searched": "Caractéristiques du paramètre recherché", + + "Ytarget": "Valeur du paramètre cible", + "Xinit": "Valeur initiale du paramètre recherché", + + "select_target_nub": "Module et paramètre à calculer", + "select_searched_param": "Paramètre recherché" +} \ No newline at end of file diff --git a/src/app/formulaire/definition/concrete/form-solveur.ts b/src/app/formulaire/definition/concrete/form-solveur.ts new file mode 100644 index 000000000..09d2fe6ab --- /dev/null +++ b/src/app/formulaire/definition/concrete/form-solveur.ts @@ -0,0 +1,53 @@ +import { IObservable } from "jalhyd"; + +import { FormulaireBase } from "./form-base"; +import { FieldSet } from "../../fieldset"; + +/** + * Formulaire pour les Solveurs + */ +export class FormulaireSolveur extends FormulaireBase { + + /** id of select configuring target Nub */ + private _targetNubSelectId: string; + + /** id of select configuring searched param */ + private _searchedParamSelectId: string; + + protected parseOptions(json: {}) { + super.parseOptions(json); + this._targetNubSelectId = this.getOption(json, "targetNubSelectId"); + this._searchedParamSelectId = this.getOption(json, "searchedParamSelectId"); + } + + public afterParseFieldset(fs: FieldSet) { + if (this._searchedParamSelectId) { + const sel = fs.getFormulaireNodeById(this._searchedParamSelectId); + if (sel) { + fs.properties.addObserver(this); + } + } + if (this._targetNubSelectId) { + const sel = fs.getFormulaireNodeById(this._targetNubSelectId); + if (sel) { + fs.properties.addObserver(this); + } + } + } + + // interface Observer + + public update(sender: IObservable, data: any) { + super.update(sender, data); + console.log("FormulaireSolveur().update", sender.constructor.name, data); + if (data.action === "propertyChange") { + if (data.name === "gridType") { + this.reset(); + // Inclined grids have more input fields (OEntH and cIncl) + this.getFieldsetById("fs_grille").updateFields(); + // Alpha and Beta are not always shown + this.getFieldsetById("fs_plan").updateFields(); + } + } + } +} diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts index b5d4978c5..07c6d3be1 100644 --- a/src/app/formulaire/fieldset.ts +++ b/src/app/formulaire/fieldset.ts @@ -10,6 +10,7 @@ import { GrilleType, GrilleProfile, BiefRegime, + Solveur, } from "jalhyd"; import { FormulaireElement } from "./formulaire-element"; @@ -258,6 +259,26 @@ export class FieldSet extends FormulaireElement implements Observer { case "fs_water_line": // Bief this.setSelectValueFromProperty("select_regime", "regime"); break; + + case "fs_target": // Solveur + this.setSelectValueFromProperty("select_target_nub", "nubToCalculate"); + break; + + case "fs_searched": // Solveur + const selectField: SelectField = this.getFormulaireNodeById("select_searched_param") as SelectField; + if (selectField) { + const nub = this.parentForm.currentNub as Solveur; + const X: ParamDefinition = nub.searchedParameter; + if (X !== undefined) { + const selectElement = selectField.getSelectedEntryFromValue(X); + try { + selectField.setValue(selectElement); + } catch (e) { + console.error(`fieldset.updateFields(): cannot set ${X.parentNub.uid}/${X.symbol} on <select> "select_searched_param"`); + } + } + } + break; } } diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts index 308b319b0..065b09633 100644 --- a/src/app/formulaire/select-field.ts +++ b/src/app/formulaire/select-field.ts @@ -8,7 +8,10 @@ import { StructureType, LoiDebit, GrilleType, - GrilleProfile + GrilleProfile, + Solveur, + ParamValueMode, + Session } from "jalhyd"; import { Field } from "./field"; @@ -188,6 +191,26 @@ export class SelectField extends Field { this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Fluvial, BiefRegime.Fluvial)); this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Torrentiel, BiefRegime.Torrentiel)); break; + + case "solveur_target": // Solveur, paramètre cible (à calculer) + // find all Nubs having at least one link to another Nub's result + console.log(">> update solveur targets"); + const downstreamNubs = Session.getInstance().getDownstreamNubs(); + for (const dn of downstreamNubs) { + this.addEntry(new SelectEntry(this._entriesBaseId + dn.uid, dn.uid)); + } + break; + + case "solveur_searched": // Solveur, paramètre recherché (à faire varier) + // find all non-calculated, non-linked parameters of all Nubs that + // the current "target" Nub depends on (if any) + console.log(">> update solveur searched"); + const ntc: Nub = (nub as Solveur).nubToCalculate; + const searchableParams = Solveur.getDependingNubsSearchableParams(ntc); + for (const p of searchableParams) { + this.addEntry(new SelectEntry(this._entriesBaseId + p.nubUid + "_" + p.symbol, p)); + } + break; } } } diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts index 65a60d25b..025930609 100644 --- a/src/app/services/formulaire.service.ts +++ b/src/app/services/formulaire.service.ts @@ -39,6 +39,7 @@ import { FormulaireMacrorugoCompound } from "../formulaire/definition/concrete/f import { FormulaireLechaptCalmon } from "../formulaire/definition/concrete/form-lechapt-calmon"; import { FormulaireGrille } from "../formulaire/definition/concrete/form-grille"; import { FormulaireBief } from "../formulaire/definition/concrete/form-bief"; +import { FormulaireSolveur } from "../formulaire/definition/concrete/form-solveur"; @Injectable() export class FormulaireService extends Observable { @@ -84,6 +85,7 @@ export class FormulaireService extends Observable { this.calculatorPaths[CalculatorType.Grille] = "grille"; this.calculatorPaths[CalculatorType.Pente] = "pente"; this.calculatorPaths[CalculatorType.Bief] = "bief"; + this.calculatorPaths[CalculatorType.Solveur] = "solveur"; } private get _intlService(): I18nService { @@ -329,6 +331,10 @@ export class FormulaireService extends Observable { f = new FormulaireBief(); break; + case CalculatorType.Solveur: + f = new FormulaireSolveur(); + break; + default: f = new FormulaireBase(); } diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 5a3e63c64..ad74567f5 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -431,6 +431,8 @@ "INFO_SNACKBAR_RESULTS_CALCULATED": "Results calculated for", "INFO_SNACKBAR_RESULTS_INVALIDATED": "Results invalidated for", "INFO_SNACKBAR_SETTINGS_SAVED": "Settings saved on this device", + "INFO_SOLVEUR_TITRE": "Multimodule solver", + "INFO_SOLVEUR_TITRE_COURT": "Solver", "INFO_THEME_CREDITS": "Credit", "INFO_THEME_DEVALAISON_TITRE": "Downstream migration", "INFO_THEME_DEVALAISON_DESCRIPTION": "Tools for dimensioning the structures present on the water intakes of hydroelectric power plants known as \"ichthyocompatible\" and consisting of fine grid planes associated with one or more outlets.", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index 523d6cd37..e93d09b95 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -430,6 +430,8 @@ "INFO_SNACKBAR_RESULTS_CALCULATED": "Résultats calculés pour", "INFO_SNACKBAR_RESULTS_INVALIDATED": "Résultats invalidés pour", "INFO_SNACKBAR_SETTINGS_SAVED": "Paramètres enregistrés sur cet appareil", + "INFO_SOLVEUR_TITRE": "Solveur multimodule", + "INFO_SOLVEUR_TITRE_COURT": "Solveur", "INFO_THEME_CREDITS": "Crédit", "INFO_THEME_DEVALAISON_TITRE": "Dévalaison", "INFO_THEME_DEVALAISON_DESCRIPTION": "Outils de dimensionnements des ouvrages présents sur les prises d'eau des centrales hydroélectriques dites \"ichtyocompatibles\" et constituées de plans de grilles fines associés à un ou plusieurs exutoires.", -- GitLab From 0bb707f3115680a2c1238dec3a8a18cd8f826080 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 15 Oct 2019 17:11:43 +0200 Subject: [PATCH 02/11] Input: render undefined values as "" --- src/app/components/generic-input/generic-input.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts index 2e076ed1c..f49b62c0e 100644 --- a/src/app/components/generic-input/generic-input.component.ts +++ b/src/app/components/generic-input/generic-input.component.ts @@ -220,7 +220,11 @@ export abstract class GenericInputComponent implements OnChanges { * MAJ et validation de l'UI */ protected updateAndValidateUI() { - this._uiValue = String(this.getModelValue()); + if (this.getModelValue() !== undefined) { + this._uiValue = String(this.getModelValue()); + } else { + this._uiValue = ""; + } this.validateUI(); } -- GitLab From 08bc15aef4eed9415d6a082c1bf492044ca3585e Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Wed, 16 Oct 2019 09:22:37 +0200 Subject: [PATCH 03/11] Update jalhyd_branch --- jalhyd_branch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jalhyd_branch b/jalhyd_branch index 0c6550147..4517376e6 100644 --- a/jalhyd_branch +++ b/jalhyd_branch @@ -1 +1 @@ -55-ajout-d-un-module-de-calcul-des-cotes-amont-aval-d-un-bief +152-solveur-multi-modules -- GitLab From 917fb69990d3fc0241b11b0ed66fe3e4f6afba95 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Wed, 16 Oct 2019 16:04:36 +0200 Subject: [PATCH 04/11] Solveur GUI interface for Solveur module draw Solveur relations in modules diagram view --- .../calculators/solveur/solveur.config.json | 6 +- src/app/calculators/solveur/solveur.en.json | 1 + src/app/calculators/solveur/solveur.fr.json | 1 + .../calculator.component.ts | 7 ++ .../modules-diagram.component.ts | 20 +++- .../select-field-line.component.ts | 12 ++- .../definition/concrete/form-solveur.ts | 76 +++++++++++---- .../formulaire/definition/form-definition.ts | 6 ++ src/app/formulaire/fieldset.ts | 37 +++++++- src/app/formulaire/ngparam.ts | 5 +- src/app/formulaire/select-field-nub.ts | 37 ++++++++ src/app/formulaire/select-field-parameter.ts | 36 ++++++++ src/app/formulaire/select-field-reference.ts | 92 +++++++++++++++++++ src/app/formulaire/select-field.ts | 24 +---- src/app/util.ts | 11 +++ src/locale/messages.en.json | 11 ++- src/locale/messages.fr.json | 11 ++- 17 files changed, 336 insertions(+), 57 deletions(-) create mode 100644 src/app/formulaire/select-field-nub.ts create mode 100644 src/app/formulaire/select-field-parameter.ts create mode 100644 src/app/formulaire/select-field-reference.ts diff --git a/src/app/calculators/solveur/solveur.config.json b/src/app/calculators/solveur/solveur.config.json index 7e6ac3bbd..40b0bbb8c 100644 --- a/src/app/calculators/solveur/solveur.config.json +++ b/src/app/calculators/solveur/solveur.config.json @@ -5,7 +5,8 @@ "fields": [ { "id": "select_target_nub", - "type": "select", + "type": "select_reference", + "reference": "nub", "source": "solveur_target" }, "Ytarget" @@ -17,7 +18,8 @@ "fields": [ { "id": "select_searched_param", - "type": "select", + "type": "select_reference", + "reference": "parameter", "source": "solveur_searched" }, "Xinit" diff --git a/src/app/calculators/solveur/solveur.en.json b/src/app/calculators/solveur/solveur.en.json index cbf67bcf2..df14b3764 100644 --- a/src/app/calculators/solveur/solveur.en.json +++ b/src/app/calculators/solveur/solveur.en.json @@ -4,6 +4,7 @@ "Ytarget": "Value of target parameter", "Xinit": "Initial value for searched parameter", + "X": "Value for searched parameter", "select_target_nub": "Module and parameter to calculate", "select_searched_param": "Searched parameter" diff --git a/src/app/calculators/solveur/solveur.fr.json b/src/app/calculators/solveur/solveur.fr.json index 86899fc0d..1439bd8da 100644 --- a/src/app/calculators/solveur/solveur.fr.json +++ b/src/app/calculators/solveur/solveur.fr.json @@ -4,6 +4,7 @@ "Ytarget": "Valeur du paramètre cible", "Xinit": "Valeur initiale du paramètre recherché", + "X": "Valeur du paramètre recherché", "select_target_nub": "Module et paramètre à calculer", "select_searched_param": "Paramètre recherché" diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index 82dc8d735..365fa61e7 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -376,6 +376,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe this._calculatorNameComponent.model = this._formulaire; // reload localisation in all cases this.formulaireService.loadUpdateFormulaireLocalisation(this._formulaire); + // call Form init hook + this._formulaire.onCalculatorInit(); break; } } else if (sender instanceof FormulaireDefinition) { @@ -530,6 +532,11 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe return (this.isPAB || this.isMRC); } + // true if current Nub is Solveur + public get isSolveur() { + return this.is(CalculatorType.Solveur); + } + // true if current Nub is PAB public get isPAB() { return this.is(CalculatorType.Pab); diff --git a/src/app/components/modules-diagram/modules-diagram.component.ts b/src/app/components/modules-diagram/modules-diagram.component.ts index 68297ad6c..d84bfdac7 100644 --- a/src/app/components/modules-diagram/modules-diagram.component.ts +++ b/src/app/components/modules-diagram/modules-diagram.component.ts @@ -16,7 +16,8 @@ import { LoiDebit, Nub, MacrorugoCompound, - Pab + Pab, + Solveur } from "jalhyd"; import { I18nService } from "../../services/internationalisation.service"; @@ -43,7 +44,7 @@ export class ModulesDiagramComponent implements AfterContentInit, AfterViewCheck private nativeElement: any; @ViewChild("diagram", { static: true }) - public diagram; + public diagram: any; public error: boolean; @@ -179,7 +180,7 @@ export class ModulesDiagramComponent implements AfterContentInit, AfterViewCheck // simple Nub (no children) def.push(f.uid + "(\"" + f.calculatorName + "\")"); } - // fnid all linked parameters + // find all linked parameters for (const p of nub.parameterIterator) { if (p.valueMode === ParamValueMode.LINK && p.isReferenceDefined()) { const target = p.referencedValue.nub; @@ -190,6 +191,19 @@ export class ModulesDiagramComponent implements AfterContentInit, AfterViewCheck def.push(nub.uid + "-->|" + symb + "|" + target.uid); } } + // add Solveur links + if (nub instanceof Solveur) { + const ntc = nub.nubToCalculate; + const sp = nub.searchedParameter; + const reads = this.intlService.localizeText("INFO_DIAGRAM_SOLVEUR_READS"); + const finds = this.intlService.localizeText("INFO_DIAGRAM_SOLVEUR_FINDS"); + if (ntc !== undefined) { + def.push(nub.uid + "-->|" + reads + ":" + ntc.calculatedParam.symbol + "|" + ntc.uid); + } + if (sp !== undefined) { + def.push(sp.nubUid + "-->|" + finds + ":" + sp.symbol + "|" + nub.uid); + } + } } return def.join("\n"); diff --git a/src/app/components/select-field-line/select-field-line.component.ts b/src/app/components/select-field-line/select-field-line.component.ts index 04a69146b..e9615dbed 100644 --- a/src/app/components/select-field-line/select-field-line.component.ts +++ b/src/app/components/select-field-line/select-field-line.component.ts @@ -1,8 +1,9 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input, OnInit } from "@angular/core"; import { SelectField } from "../../formulaire/select-field"; import { SelectEntry } from "../../formulaire/select-entry"; import { I18nService } from "../../services/internationalisation.service"; +import { SelectFieldReference } from "../../formulaire/select-field-reference"; @Component({ selector: "select-field-line", @@ -11,7 +12,7 @@ import { I18nService } from "../../services/internationalisation.service"; "./select-field-line.component.scss" ] }) -export class SelectFieldLineComponent { +export class SelectFieldLineComponent implements OnInit { /** aide en ligne */ protected helpLink: string | { [key: string]: string }; @@ -83,4 +84,11 @@ export class SelectFieldLineComponent { public get uitextOpenHelp() { return this.i18nService.localizeText("INFO_CALCULATOR_OPEN_HELP"); } + + // called every time we navigate to the module + ngOnInit(): void { + if (this._select instanceof SelectFieldReference) { + this._select.updateEntries(); + } + } } diff --git a/src/app/formulaire/definition/concrete/form-solveur.ts b/src/app/formulaire/definition/concrete/form-solveur.ts index 09d2fe6ab..e04fc1665 100644 --- a/src/app/formulaire/definition/concrete/form-solveur.ts +++ b/src/app/formulaire/definition/concrete/form-solveur.ts @@ -1,7 +1,9 @@ -import { IObservable } from "jalhyd"; +import { IObservable, ParamDefinition, Solveur } from "jalhyd"; import { FormulaireBase } from "./form-base"; -import { FieldSet } from "../../fieldset"; +import { SelectFieldNub } from "../../select-field-nub"; +import { SelectFieldParameter } from "../../select-field-parameter"; +import { NgParameter } from "../../ngparam"; /** * Formulaire pour les Solveurs @@ -20,33 +22,73 @@ export class FormulaireSolveur extends FormulaireBase { this._searchedParamSelectId = this.getOption(json, "searchedParamSelectId"); } - public afterParseFieldset(fs: FieldSet) { - if (this._searchedParamSelectId) { - const sel = fs.getFormulaireNodeById(this._searchedParamSelectId); + protected completeParse(json: {}) { + super.completeParse(json); + if (this._targetNubSelectId) { + const sel = this.getFormulaireNodeById(this._targetNubSelectId); if (sel) { - fs.properties.addObserver(this); + sel.addObserver(this); } } - if (this._targetNubSelectId) { - const sel = fs.getFormulaireNodeById(this._targetNubSelectId); + if (this._searchedParamSelectId) { + const sel = this.getFormulaireNodeById(this._searchedParamSelectId); if (sel) { - fs.properties.addObserver(this); + sel.addObserver(this); } } + + } + + private debugState() { + const sol = this._currentNub as Solveur; + const ntc = sol.nubToCalculate; + const spm = sol.searchedParameter; + console.log( + `ETAT:\n X.singleValue=${sol.prms.X.singleValue}\n Y.singleValue=${sol.prms.Y.singleValue}` + + `\n Xinit.singleValue=${sol.prms.Xinit.singleValue}\n Ytarget.singleValue=${sol.prms.Ytarget.singleValue}` + + `\n searchedParam.singleValue=${spm.singleValue}` + ); } // interface Observer public update(sender: IObservable, data: any) { super.update(sender, data); - console.log("FormulaireSolveur().update", sender.constructor.name, data); - if (data.action === "propertyChange") { - if (data.name === "gridType") { - this.reset(); - // Inclined grids have more input fields (OEntH and cIncl) - this.getFieldsetById("fs_grille").updateFields(); - // Alpha and Beta are not always shown - this.getFieldsetById("fs_plan").updateFields(); + if (sender instanceof SelectFieldNub) { + if (data.action === "select") { + // update Solveur property: Nub to calculate + console.log("(i) update Nub to calculate"); + try { + // if searchedParam is set to a value that won't be available anymore + // once nubToCalculate is updated, setPropValue throws an error, but + // nubToCalculate is updated anyway; here, just inhibit the error + this._currentNub.properties.setPropValue("nubToCalculate", data.value.value); + } catch (e) { } + // refresh parameters selector + const sel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldParameter; + if (sel) { + console.log("(ii) update Parameters entries"); + sel.updateEntries(); + this.debugState(); + // reflect changes in GUI + const inputYtarget = this.getFormulaireNodeById("Ytarget") as NgParameter; + inputYtarget.notifyValueModified(this); + } + } + } + if (sender instanceof SelectFieldParameter) { + if (data.action === "select") { + // update Solveur property: searched Parameter + console.log("(i) update searched Parameter"); + const p: ParamDefinition = data.value.value; + this._currentNub.properties.setPropValue( + "searchedParameter", + p.nubUid + "/" + p.symbol + ); + this.debugState(); + // reflect changes in GUI + const inputXinit = this.getFormulaireNodeById("Xinit") as NgParameter; + inputXinit.notifyValueModified(this); } } } diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts index b178d3a3d..5aa007c8a 100644 --- a/src/app/formulaire/definition/form-definition.ts +++ b/src/app/formulaire/definition/form-definition.ts @@ -452,6 +452,12 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs return new TopFormulaireElementIterator(this); } + /** + * Appelé par CalculatorComponent lrosque le Formulaire est chargé dans la vue, + * c'est à dire lorsqu'on affiche un module de calcul à l'écran + */ + public onCalculatorInit() {} + // interface Observer public update(sender: any, data: any) { diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts index 07c6d3be1..e892c0c2c 100644 --- a/src/app/formulaire/fieldset.ts +++ b/src/app/formulaire/fieldset.ts @@ -21,6 +21,8 @@ import { FormulaireDefinition } from "./definition/form-definition"; import { StringMap } from "../stringmap"; import { FormulaireNode } from "./formulaire-node"; import { FieldsetContainer } from "./fieldset-container"; +import { SelectFieldNub } from "./select-field-nub"; +import { SelectFieldParameter } from "./select-field-parameter"; export class FieldSet extends FormulaireElement implements Observer { /** @@ -97,6 +99,28 @@ export class FieldSet extends FormulaireElement implements Observer { return res; } + private parse_select_reference(json: {}): SelectField { + const refType = json["reference"]; + const source = json["source"]; + let res: SelectField; + if (source === undefined || source === "") { + throw new Error(`Fieldset.parse_select_reference(): "source" must not be empty`); + } + switch (refType) { + case "nub": // @TODO upstreamNub / downstreamNub ? + res = new SelectFieldNub(this, source); + break; + case "parameter": + res = new SelectFieldParameter(this, source); + break; + default: + throw new Error(`Fieldset.parse_select_reference(): unknown reference type ${refType}`); + } + res.parseConfig(json); + res.addObserver(this); + return res; + } + public get properties(): Props { return this.nub.properties; } @@ -186,6 +210,11 @@ export class FieldSet extends FormulaireElement implements Observer { this.addField(param); break; + case "select_reference": + param = this.parse_select_reference(field); + this.addField(param); + break; + } } } @@ -260,7 +289,7 @@ export class FieldSet extends FormulaireElement implements Observer { this.setSelectValueFromProperty("select_regime", "regime"); break; - case "fs_target": // Solveur + /* case "fs_target": // Solveur this.setSelectValueFromProperty("select_target_nub", "nubToCalculate"); break; @@ -274,11 +303,13 @@ export class FieldSet extends FormulaireElement implements Observer { try { selectField.setValue(selectElement); } catch (e) { - console.error(`fieldset.updateFields(): cannot set ${X.parentNub.uid}/${X.symbol} on <select> "select_searched_param"`); + console.error( + `fieldset.updateFields(): cannot set ${X.parentNub.uid}/${X.symbol} on <select> "select_searched_param"` + ); } } } - break; + break; */ } } diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts index 5387c058d..b5d15d216 100644 --- a/src/app/formulaire/ngparam.ts +++ b/src/app/formulaire/ngparam.ts @@ -113,9 +113,8 @@ export class NgParameter extends InputField implements Observer { const cVal = ref.nub.result.getCalculatedValues(); valuePreview = fv(cVal[0]) + " … " + fv(cVal[cVal.length - 1]); } else { - const vCalc = ref.nub.result.vCalc; - if (vCalc) { - valuePreview = fv(vCalc); + if (ref.nub.result.resultElements.length > 0 && ref.nub.result.vCalc) { + valuePreview = fv(ref.nub.result.vCalc); } else { // computation has been run but has failed valuePreview = i18n.localizeText("INFO_PARAMFIELD_CALCULATION_FAILED"); diff --git a/src/app/formulaire/select-field-nub.ts b/src/app/formulaire/select-field-nub.ts new file mode 100644 index 000000000..345699fc1 --- /dev/null +++ b/src/app/formulaire/select-field-nub.ts @@ -0,0 +1,37 @@ +import { SelectFieldReference } from "./select-field-reference"; +import { SelectEntry } from "./select-entry"; +import { ServiceFactory } from "../services/service-factory"; +import { decodeHtml } from "../util"; + +import { Session } from "jalhyd"; + +/** + * A select field that populates itself with references to Nubs + */ +export class SelectFieldNub extends SelectFieldReference { + + protected initSelectedValue() {} + + /** + * Populates entries with available references + */ + protected populate() { + switch (this._source) { + case "solveur_target": // Solveur, paramètre cible (à calculer) + // find all Nubs having at least one link to another Nub's result + const fs = ServiceFactory.instance.formulaireService; + const downstreamNubs = Session.getInstance().getDownstreamNubs(); + for (const dn of downstreamNubs) { + const calc = fs.getFormulaireFromId(dn.uid).calculatorName; + let label = calc; + if (dn.calculatedParam !== undefined) { + const varName = fs.expandVariableName(dn.calcType, dn.calculatedParam.symbol); + label += ` / ${varName} (${dn.calculatedParam.symbol})`; + } + this.addEntry(new SelectEntry(this._entriesBaseId + dn.uid, dn.uid, decodeHtml(label))); + } + break; + } + } + +} diff --git a/src/app/formulaire/select-field-parameter.ts b/src/app/formulaire/select-field-parameter.ts new file mode 100644 index 000000000..d84ad50ac --- /dev/null +++ b/src/app/formulaire/select-field-parameter.ts @@ -0,0 +1,36 @@ +import { SelectFieldReference } from "./select-field-reference"; +import { SelectEntry } from "./select-entry"; +import { decodeHtml } from "../util"; +import { ServiceFactory } from "../services/service-factory"; + +import { Nub, Solveur } from "jalhyd"; + +/** + * A select field that populates itself with references to ParamDefinitions + */ +export class SelectFieldParameter extends SelectFieldReference { + + protected initSelectedValue() {} + + /** + * Populates entries with available references + */ + protected populate() { + switch (this._source) { + case "solveur_searched": // Solveur, paramètre recherché (à faire varier) + // find all non-calculated, non-linked parameters of all Nubs that + // the current "target" Nub depends on (if any) + const fs = ServiceFactory.instance.formulaireService; + const ntc: Nub = (this.parentForm.currentNub as Solveur).nubToCalculate; + const searchableParams = Solveur.getDependingNubsSearchableParams(ntc); + for (const p of searchableParams) { + const calc = fs.getFormulaireFromId(p.parentNub.uid).calculatorName; + const varName = fs.expandVariableName(p.parentNub.calcType, p.symbol); + const label = `${p.symbol} - ${varName} (${calc})`; + this.addEntry(new SelectEntry(this._entriesBaseId + p.nubUid + "_" + p.symbol, p, decodeHtml(label))); + } + break; + } + } + +} diff --git a/src/app/formulaire/select-field-reference.ts b/src/app/formulaire/select-field-reference.ts new file mode 100644 index 000000000..a84b272ea --- /dev/null +++ b/src/app/formulaire/select-field-reference.ts @@ -0,0 +1,92 @@ +import { SelectField } from "./select-field"; +import { SelectEntry } from "./select-entry"; +import { FormulaireNode } from "./formulaire-node"; + +/** + * A select field that populates itself with references to + * available objects (for ex. Nub or ParamDefinition) + */ +export abstract class SelectFieldReference extends SelectField { + + /** source identifier for populate() method */ + protected _source: string; + + constructor(parent: FormulaireNode, source: string) { + super(parent); + this._source = source; + this.initSelectedValue(); + } + + protected abstract initSelectedValue(); + + /** + * Populates entries with available references + */ + protected abstract populate(); + + /** + * Reloads available entries, trying to keep the current selected + * value; does not notify observers if value did not change + */ + public updateEntries() { + // store previous selected entry + const pse = this._selectedEntry; + // empty + this.clearEntries(); + // populate + this.populate(); + // keep previously selected entry if possible + if (pse && pse.id) { + this.setValueFromId(pse.id); + } + // if no entry is available anymore, unset value + if (this.entries.length === 0) { + super.setValue(undefined); + } + } + + /** + * Updates selectedValue; notifies observers only if + * value.id has changed + */ + public setValue(v: SelectEntry) { + const previousSelectedEntry = this._selectedEntry; + this._selectedEntry = v; + if ( + ! previousSelectedEntry + || (previousSelectedEntry.id !== v.id) + ) { + console.log(`--> select, setValue: ${v.value}`); + this.notifyObservers({ + "action": "select", + "value": v + }, this); + } + } + + /** + * Sets value from given ID; if it was not found, sets the + * first available entry as selectedValue + */ + public setValueFromId(id: string) { + let found = false; + for (const e of this._entries) { + if (e.id === id) { + found = true; + this.setValue(e); + } + } + if (! found) { + // default to first available entry if any + if (this._entries.length > 0) { + this.setValue(this._entries[0]); + } else { + // notify observers that no value is selected anymore + this.notifyObservers({ + "action": "select", + "value": undefined + }, this); + } + } + } +} diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts index 065b09633..66545a058 100644 --- a/src/app/formulaire/select-field.ts +++ b/src/app/formulaire/select-field.ts @@ -108,7 +108,9 @@ export class SelectField extends Field { public updateLocalisation(loc: StringMap) { super.updateLocalisation(loc); for (const e of this._entries) { - e.label = loc[e.id]; + if (loc[e.id] !== undefined) { + e.label = loc[e.id]; + } } } @@ -191,26 +193,6 @@ export class SelectField extends Field { this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Fluvial, BiefRegime.Fluvial)); this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Torrentiel, BiefRegime.Torrentiel)); break; - - case "solveur_target": // Solveur, paramètre cible (à calculer) - // find all Nubs having at least one link to another Nub's result - console.log(">> update solveur targets"); - const downstreamNubs = Session.getInstance().getDownstreamNubs(); - for (const dn of downstreamNubs) { - this.addEntry(new SelectEntry(this._entriesBaseId + dn.uid, dn.uid)); - } - break; - - case "solveur_searched": // Solveur, paramètre recherché (à faire varier) - // find all non-calculated, non-linked parameters of all Nubs that - // the current "target" Nub depends on (if any) - console.log(">> update solveur searched"); - const ntc: Nub = (nub as Solveur).nubToCalculate; - const searchableParams = Solveur.getDependingNubsSearchableParams(ntc); - for (const p of searchableParams) { - this.addEntry(new SelectEntry(this._entriesBaseId + p.nubUid + "_" + p.symbol, p)); - } - break; } } } diff --git a/src/app/util.ts b/src/app/util.ts index 6d6a10bb1..e4d3e1f1b 100644 --- a/src/app/util.ts +++ b/src/app/util.ts @@ -30,3 +30,14 @@ export function fv(p: NgParameter | number): string { return formattedValue(value, nDigits); } + +/** + * Trick to decode HTML entities in a string + * https://stackoverflow.com/a/7394787/5986614 + * @param html string containing HTML entities, like + */ +export function decodeHtml(html: string): string { + const txt = document.createElement("textarea"); + txt.innerHTML = html; + return txt.value; +} diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index ad74567f5..270145ca4 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -4,7 +4,7 @@ "WARNING_DOWNSTREAM_ELEVATION_POSSIBLE_SUBMERSION": "Downstream elevation is higher than weir elevation (possible submersion)", "WARNING_NOTCH_SUBMERSION_GREATER_THAN_07": "Notch formula is discouraged when submersion is greater than 0.7", "WARNING_SLOT_SUBMERSION_NOT_BETWEEN_07_AND_09": "Slot formula is discouraged when submersion is lower than 0.7 or greater than 0.9", - "ERROR_ABSTRACT": "%nb% errors occurred during calculation", + "WARNING_ERRORS_ABSTRACT": "%nb% errors occurred during calculation", "ERROR_BIEF_Z1_CALC_FAILED": "Unable to calculate upstream elevation (calculation interrupted before upstream)", "ERROR_BIEF_Z2_CALC_FAILED": "Unable to calculate downstream elevation (calculation interrupted before downstream)", "ERROR_DICHO_CONVERGE": "Dichotomy could not converge", @@ -15,6 +15,8 @@ "ERROR_DICHO_TARGET_TOO_HIGH": "Dichotomy: the solution %targetSymbol%=%targetValue% is greater than the maximum computable value %targetSymbol%(%variableSymbol%=%variableExtremeValue%)=%extremeTarget%)", "ERROR_DICHO_TARGET_TOO_LOW": "Dichotomy: the solution %targetSymbol%=%targetValue% is lower than the minimum computable value %targetSymbol%(%variableSymbol%=%variableExtremeValue%)=%extremeTarget%)", "ERROR_ELEVATION_ZI_LOWER_THAN_Z2": "Upstream elevation is lower than downstream elevation", + "ERROR_IN_CALC_CHAIN": "An error occurred in calculation chain", + "WARNING_ERROR_IN_CALC_CHAIN_STEPS": "Errors occurred during chain calculation", "ERROR_INTERVAL_OUTSIDE": "Interval: value %value% is outside of %interval%", "ERROR_INTERVAL_UNDEF": "Interval: invalid 'undefined' value", "ERROR_INVALID_AT_POSITION": "Position %s:", @@ -80,6 +82,8 @@ "INFO_COURBEREMOUS_TITRE": "Backwater curves", "INFO_DEVER_TITRE_COURT": "Free weir", "INFO_DEVER_TITRE": "Free flow weir stage-discharge laws", + "INFO_DIAGRAM_SOLVEUR_FINDS": "finds", + "INFO_DIAGRAM_SOLVEUR_READS": "reads", "INFO_DIAGRAM_TITLE": "Calculation modules diagram", "INFO_DIAGRAM_DRAWING_ERROR": "Error while drawing diagram", "INFO_DIAGRAM_CALCULATED_PARAM": "calculated parameter", @@ -455,7 +459,7 @@ "INFO_EXAMPLE_LABEL_PAB_COMPLETE": "Standard fish ladder", "INFO_EXAMPLES_TITLE": "Examples", "INFO_EXAMPLES_SUBTITLE": "Load standard examples", - "WARNING_ABSTRACT": "%nb% warnings occurred during calculation", + "WARNING_WARNINGS_ABSTRACT": "%nb% warnings occurred during calculation", "WARNING_REMOUS_ARRET_CRITIQUE": "Calculation stopped: critical elevation reached at abscissa %x%", "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p must not be greater than 2.5. h/p is forced to 2.5", "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "Threshold height should be greater than 0.1 m. Beta coefficient is forced to 0", @@ -468,5 +472,6 @@ "WARNING_DOWNSTREAM_BOTTOM_HIGHER_THAN_WATER": "Downstream water elevation is lower or equal to bottom elevation", "WARNING_YN_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "Normal depth: slope is negative or zero, normal depth is infinite", "WARNING_YN_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE": "Normal depth: non convergence of the calculation (Newton's method)", - "WARNING_SESSION_LOAD_NOTES_MERGED": "Notes have been merged" + "WARNING_SESSION_LOAD_NOTES_MERGED": "Notes have been merged", + "WARNING_VALUE_ROUNDED_TO_INTEGER": "Value of %symbol% was rounded to %rounded%" } diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index e93d09b95..e04f71c07 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -4,7 +4,7 @@ "WARNING_DOWNSTREAM_ELEVATION_POSSIBLE_SUBMERSION": "La cote de l'eau aval est plus élevée que la cote du seuil (ennoiement possible)", "WARNING_NOTCH_SUBMERSION_GREATER_THAN_07": "La formule de l'échancrure n'est pas conseillée pour un ennoiement supérieur à 0.7", "WARNING_SLOT_SUBMERSION_NOT_BETWEEN_07_AND_09": "La formule de la fente n'est pas conseillée pour un ennoiement inférieur à 0.7 et supérieur à 0.9", - "ERROR_ABSTRACT": "%nb% erreurs rencontrées lors du calcul", + "WARNING_ERRORS_ABSTRACT": "%nb% erreurs rencontrées lors du calcul", "ERROR_BIEF_Z1_CALC_FAILED": "Impossible de calculer la cote amont (calcul interrompu avant l'amont)", "ERROR_BIEF_Z2_CALC_FAILED": "Impossible de calculer la cote aval (calcul interrompu avant l'aval)", "ERROR_DICHO_CONVERGE": "La dichotomie n'a pas pu converger", @@ -15,6 +15,8 @@ "ERROR_DICHO_TARGET_TOO_HIGH": "Dichotomie : la solution %targetSymbol%=%targetValue% est supérieure à la valeur maximale calculable %targetSymbol%(%variableSymbol%=%variableExtremeValue%)=%extremeTarget%)", "ERROR_DICHO_TARGET_TOO_LOW": "Dichotomie : la solution %targetSymbol%=%targetValue% est inférieure à la valeur minimale calculable %targetSymbol%(%variableSymbol%=%variableExtremeValue%)=%extremeTarget%)", "ERROR_ELEVATION_ZI_LOWER_THAN_Z2": "La cote amont est plus basse que la cote aval", + "ERROR_IN_CALC_CHAIN": "Une erreur est survenue dans la chaîne de calcul", + "WARNING_ERROR_IN_CALC_CHAIN_STEPS": "Des erreurs sont survenues durant le calcul en chaîne", "ERROR_INTERVAL_OUTSIDE": "Intervalle : la valeur %value% est hors de l'intervalle %interval%", "ERROR_INTERVAL_UNDEF": "Interval : valeur 'undefined' incorrecte", "ERROR_INVALID_AT_POSITION": "Position %s :", @@ -80,6 +82,8 @@ "INFO_COURBEREMOUS_TITRE": "Courbes de remous", "INFO_DEVER_TITRE_COURT": "Déver. dénoyés", "INFO_DEVER_TITRE": "Lois de déversoirs dénoyés", + "INFO_DIAGRAM_SOLVEUR_FINDS": "trouve", + "INFO_DIAGRAM_SOLVEUR_READS": "lit", "INFO_DIAGRAM_TITLE": "Diagramme des modules de calcul", "INFO_DIAGRAM_DRAWING_ERROR": "Erreur lors du dessin du diagramme", "INFO_DIAGRAM_CALCULATED_PARAM": "paramètre calculé", @@ -454,7 +458,7 @@ "INFO_EXAMPLE_LABEL_PAB_COMPLETE": "Passe à bassins type", "INFO_EXAMPLES_TITLE": "Exemples", "INFO_EXAMPLES_SUBTITLE": "Charger des exemples types", - "WARNING_ABSTRACT": "%nb% avertissements rencontrés lors du calcul", + "WARNING_WARNINGS_ABSTRACT": "%nb% avertissements rencontrés lors du calcul", "WARNING_REMOUS_ARRET_CRITIQUE": "Arrêt du calcul : hauteur critique atteinte à l'abscisse %x%", "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p ne doit pas être supérieur à 2,5. h/p est forcé à 2,5", "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "La pelle du seuil doit mesurer au moins 0,1 m. Le coefficient béta est forcé à 0", @@ -467,5 +471,6 @@ "WARNING_DOWNSTREAM_BOTTOM_HIGHER_THAN_WATER": "La cote de l'eau à l'aval est plus basse ou égale à la cote de fond", "WARNING_YN_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "Hauteur normale: pente négative ou nulle, hauteur normale infinie", "WARNING_YN_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE": "Hauteur normale: non convergence du calcul (méthode de Newton)", - "WARNING_SESSION_LOAD_NOTES_MERGED": "Les notes ont été fusionnées" + "WARNING_SESSION_LOAD_NOTES_MERGED": "Les notes ont été fusionnées", + "WARNING_VALUE_ROUNDED_TO_INTEGER": "La valeur de %symbol% a été arrondie à %rounded%" } -- GitLab From fd05a5a0c97baf3d31fd086939dc27bbfc83d1f1 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Mon, 21 Oct 2019 17:06:51 +0200 Subject: [PATCH 05/11] Matomo: do not track "calculator" page views anymore --- src/app/components/generic-calculator/calculator.component.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index 365fa61e7..18b6f57aa 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -122,8 +122,6 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe this.intlService = ServiceFactory.instance.i18nService; this.formulaireService = ServiceFactory.instance.formulaireService; - this.matomoTracker.trackPageView("calculator"); - // hotkeys listeners this.hotkeysService.add(new Hotkey("alt+w", AppComponent.onHotkey(this.closeCalculator, this))); this.hotkeysService.add(new Hotkey("alt+d", AppComponent.onHotkey(this.cloneCalculator, this))); -- GitLab From 8ae2133bfbb9d75cc64accc1f2cfda1c1ddda46b Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Mon, 21 Oct 2019 17:25:34 +0200 Subject: [PATCH 06/11] Update translations --- src/locale/messages.en.json | 2 +- src/locale/messages.fr.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 270145ca4..b6db3ee32 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -7,7 +7,7 @@ "WARNING_ERRORS_ABSTRACT": "%nb% errors occurred during calculation", "ERROR_BIEF_Z1_CALC_FAILED": "Unable to calculate upstream elevation (calculation interrupted before upstream)", "ERROR_BIEF_Z2_CALC_FAILED": "Unable to calculate downstream elevation (calculation interrupted before downstream)", - "ERROR_DICHO_CONVERGE": "Dichotomy could not converge", + "ERROR_DICHO_CONVERGE": "Dichotomy could not converge. Last approximation: %lastApproximation%", "ERROR_DICHO_FUNCTION_VARIATION": "unable to determinate function direction of variation", "ERROR_DICHO_INIT_DOMAIN": "Dichotomy: target %targetSymbol%=%targetValue% does not exist for variable %variableSymbol% valued in interval %variableInterval%", "ERROR_DICHO_INVALID_STEP_GROWTH": "Dichotomy (initial interval search): invalid null step growth", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index e04f71c07..f7dd7975f 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -7,7 +7,7 @@ "WARNING_ERRORS_ABSTRACT": "%nb% erreurs rencontrées lors du calcul", "ERROR_BIEF_Z1_CALC_FAILED": "Impossible de calculer la cote amont (calcul interrompu avant l'amont)", "ERROR_BIEF_Z2_CALC_FAILED": "Impossible de calculer la cote aval (calcul interrompu avant l'aval)", - "ERROR_DICHO_CONVERGE": "La dichotomie n'a pas pu converger", + "ERROR_DICHO_CONVERGE": "La dichotomie n'a pas pu converger. Dernière approximation: %lastApproximation%", "ERROR_DICHO_FUNCTION_VARIATION": "Dichotomie : impossible de determiner le sens de variation de la fonction", "ERROR_DICHO_INIT_DOMAIN": "Dichotomie : la valeur cible %targetSymbol%=%targetValue% n'existe pas pour la variable %variableSymbol% prise dans l'intervalle %variableInterval%", "ERROR_DICHO_INVALID_STEP_GROWTH": "Dichotomie : l'augmentation du pas pour la recherche de l'intervalle de départ est incorrecte (=0)", -- GitLab From 895a29f79266994f54f0d83aff169d24a39f47b2 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 22 Oct 2019 10:19:45 +0200 Subject: [PATCH 07/11] Solveur: properly set targets initial values on session loading --- src/app/formulaire/fieldset.ts | 22 -------------------- src/app/formulaire/select-field-nub.ts | 10 +++++++-- src/app/formulaire/select-field-parameter.ts | 8 ++++++- src/app/formulaire/select-field-reference.ts | 10 ++++++++- src/app/formulaire/select-field.ts | 7 +++++++ 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts index e892c0c2c..068116458 100644 --- a/src/app/formulaire/fieldset.ts +++ b/src/app/formulaire/fieldset.ts @@ -288,28 +288,6 @@ export class FieldSet extends FormulaireElement implements Observer { case "fs_water_line": // Bief this.setSelectValueFromProperty("select_regime", "regime"); break; - - /* case "fs_target": // Solveur - this.setSelectValueFromProperty("select_target_nub", "nubToCalculate"); - break; - - case "fs_searched": // Solveur - const selectField: SelectField = this.getFormulaireNodeById("select_searched_param") as SelectField; - if (selectField) { - const nub = this.parentForm.currentNub as Solveur; - const X: ParamDefinition = nub.searchedParameter; - if (X !== undefined) { - const selectElement = selectField.getSelectedEntryFromValue(X); - try { - selectField.setValue(selectElement); - } catch (e) { - console.error( - `fieldset.updateFields(): cannot set ${X.parentNub.uid}/${X.symbol} on <select> "select_searched_param"` - ); - } - } - } - break; */ } } diff --git a/src/app/formulaire/select-field-nub.ts b/src/app/formulaire/select-field-nub.ts index 345699fc1..359addcd7 100644 --- a/src/app/formulaire/select-field-nub.ts +++ b/src/app/formulaire/select-field-nub.ts @@ -3,14 +3,20 @@ import { SelectEntry } from "./select-entry"; import { ServiceFactory } from "../services/service-factory"; import { decodeHtml } from "../util"; -import { Session } from "jalhyd"; +import { Session, Solveur } from "jalhyd"; /** * A select field that populates itself with references to Nubs */ export class SelectFieldNub extends SelectFieldReference { - protected initSelectedValue() {} + protected initSelectedValue() { + const nub = this.parentForm.currentNub; + if (nub instanceof Solveur) { + const ntc = nub.nubToCalculate; + this.setValueFromId(this._entriesBaseId + ntc.uid); + } + } /** * Populates entries with available references diff --git a/src/app/formulaire/select-field-parameter.ts b/src/app/formulaire/select-field-parameter.ts index d84ad50ac..7ac211949 100644 --- a/src/app/formulaire/select-field-parameter.ts +++ b/src/app/formulaire/select-field-parameter.ts @@ -10,7 +10,13 @@ import { Nub, Solveur } from "jalhyd"; */ export class SelectFieldParameter extends SelectFieldReference { - protected initSelectedValue() {} + protected initSelectedValue() { + const nub = this.parentForm.currentNub; + if (nub instanceof Solveur) { + const sp = nub.searchedParameter; + this.setValueFromId(this._entriesBaseId + sp.nubUid + "_" + sp.symbol); + } + } /** * Populates entries with available references diff --git a/src/app/formulaire/select-field-reference.ts b/src/app/formulaire/select-field-reference.ts index a84b272ea..2d715850a 100644 --- a/src/app/formulaire/select-field-reference.ts +++ b/src/app/formulaire/select-field-reference.ts @@ -14,7 +14,6 @@ export abstract class SelectFieldReference extends SelectField { constructor(parent: FormulaireNode, source: string) { super(parent); this._source = source; - this.initSelectedValue(); } protected abstract initSelectedValue(); @@ -24,6 +23,15 @@ export abstract class SelectFieldReference extends SelectField { */ protected abstract populate(); + /** + * Once config is parsed, init original value from model + * (needs config, for this._entriesBaseId to be set) + */ + protected afterParseConfig() { + this.populate(); + this.initSelectedValue(); + } + /** * Reloads available entries, trying to keep the current selected * value; does not notify observers if value did not change diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts index 66545a058..b2ea54a8c 100644 --- a/src/app/formulaire/select-field.ts +++ b/src/app/formulaire/select-field.ts @@ -77,6 +77,11 @@ export class SelectField extends Field { */ protected populate() { } + /** + * Triggered at the end of parseConfig() + */ + protected afterParseConfig() { } + public getSelectedEntryFromValue(val: any): SelectEntry { for (const se of this._entries) { if (se.value === val) { @@ -194,5 +199,7 @@ export class SelectField extends Field { this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Torrentiel, BiefRegime.Torrentiel)); break; } + + this.afterParseConfig(); } } -- GitLab From bc837da2962e3115beaa4327e1b2f55125ba5b82 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 22 Oct 2019 11:50:35 +0200 Subject: [PATCH 08/11] Solveur: fix select update mechanism --- .../definition/concrete/form-solveur.ts | 14 +++--- src/app/formulaire/select-field-nub.ts | 4 +- src/app/formulaire/select-field-parameter.ts | 4 +- src/app/formulaire/select-field-reference.ts | 45 +++++++++++-------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/app/formulaire/definition/concrete/form-solveur.ts b/src/app/formulaire/definition/concrete/form-solveur.ts index e04fc1665..e472d035f 100644 --- a/src/app/formulaire/definition/concrete/form-solveur.ts +++ b/src/app/formulaire/definition/concrete/form-solveur.ts @@ -28,12 +28,16 @@ export class FormulaireSolveur extends FormulaireBase { const sel = this.getFormulaireNodeById(this._targetNubSelectId); if (sel) { sel.addObserver(this); + // force 1st observation + (sel as SelectFieldNub).notifySelectValueChanged(); } } if (this._searchedParamSelectId) { const sel = this.getFormulaireNodeById(this._searchedParamSelectId); if (sel) { sel.addObserver(this); + // force 1st observation + (sel as SelectFieldNub).notifySelectValueChanged(); } } @@ -41,12 +45,11 @@ export class FormulaireSolveur extends FormulaireBase { private debugState() { const sol = this._currentNub as Solveur; - const ntc = sol.nubToCalculate; const spm = sol.searchedParameter; console.log( - `ETAT:\n X.singleValue=${sol.prms.X.singleValue}\n Y.singleValue=${sol.prms.Y.singleValue}` + `ETAT:\n X.singleValue=${sol.prms.X ? sol.prms.X.singleValue : "UNDEF"}\n Y.singleValue=${sol.prms.Y.singleValue}` + `\n Xinit.singleValue=${sol.prms.Xinit.singleValue}\n Ytarget.singleValue=${sol.prms.Ytarget.singleValue}` - + `\n searchedParam.singleValue=${spm.singleValue}` + + `\n searchedParam.singleValue=${spm ? spm.singleValue : "UNDEF"}` ); } @@ -57,7 +60,6 @@ export class FormulaireSolveur extends FormulaireBase { if (sender instanceof SelectFieldNub) { if (data.action === "select") { // update Solveur property: Nub to calculate - console.log("(i) update Nub to calculate"); try { // if searchedParam is set to a value that won't be available anymore // once nubToCalculate is updated, setPropValue throws an error, but @@ -67,9 +69,7 @@ export class FormulaireSolveur extends FormulaireBase { // refresh parameters selector const sel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldParameter; if (sel) { - console.log("(ii) update Parameters entries"); sel.updateEntries(); - this.debugState(); // reflect changes in GUI const inputYtarget = this.getFormulaireNodeById("Ytarget") as NgParameter; inputYtarget.notifyValueModified(this); @@ -79,13 +79,11 @@ export class FormulaireSolveur extends FormulaireBase { if (sender instanceof SelectFieldParameter) { if (data.action === "select") { // update Solveur property: searched Parameter - console.log("(i) update searched Parameter"); const p: ParamDefinition = data.value.value; this._currentNub.properties.setPropValue( "searchedParameter", p.nubUid + "/" + p.symbol ); - this.debugState(); // reflect changes in GUI const inputXinit = this.getFormulaireNodeById("Xinit") as NgParameter; inputXinit.notifyValueModified(this); diff --git a/src/app/formulaire/select-field-nub.ts b/src/app/formulaire/select-field-nub.ts index 359addcd7..b19492cc0 100644 --- a/src/app/formulaire/select-field-nub.ts +++ b/src/app/formulaire/select-field-nub.ts @@ -14,7 +14,9 @@ export class SelectFieldNub extends SelectFieldReference { const nub = this.parentForm.currentNub; if (nub instanceof Solveur) { const ntc = nub.nubToCalculate; - this.setValueFromId(this._entriesBaseId + ntc.uid); + if (ntc !== undefined) { + this.setValueFromId(this._entriesBaseId + ntc.uid); + } } } diff --git a/src/app/formulaire/select-field-parameter.ts b/src/app/formulaire/select-field-parameter.ts index 7ac211949..b979a98f7 100644 --- a/src/app/formulaire/select-field-parameter.ts +++ b/src/app/formulaire/select-field-parameter.ts @@ -14,7 +14,9 @@ export class SelectFieldParameter extends SelectFieldReference { const nub = this.parentForm.currentNub; if (nub instanceof Solveur) { const sp = nub.searchedParameter; - this.setValueFromId(this._entriesBaseId + sp.nubUid + "_" + sp.symbol); + if (sp !== undefined) { + this.setValueFromId(this._entriesBaseId + sp.nubUid + "_" + sp.symbol); + } } } diff --git a/src/app/formulaire/select-field-reference.ts b/src/app/formulaire/select-field-reference.ts index 2d715850a..3a0d8396c 100644 --- a/src/app/formulaire/select-field-reference.ts +++ b/src/app/formulaire/select-field-reference.ts @@ -46,10 +46,13 @@ export abstract class SelectFieldReference extends SelectField { // keep previously selected entry if possible if (pse && pse.id) { this.setValueFromId(pse.id); - } - // if no entry is available anymore, unset value - if (this.entries.length === 0) { - super.setValue(undefined); + } else { + // if no entry is available anymore, unset value + if (this.entries.length === 0) { + super.setValue(undefined); + } else { + this.setDefaultValue(); + } } } @@ -64,14 +67,17 @@ export abstract class SelectFieldReference extends SelectField { ! previousSelectedEntry || (previousSelectedEntry.id !== v.id) ) { - console.log(`--> select, setValue: ${v.value}`); - this.notifyObservers({ - "action": "select", - "value": v - }, this); + this.notifySelectValueChanged(); } } + public notifySelectValueChanged() { + this.notifyObservers({ + "action": "select", + "value": this._selectedEntry + }, this); + } + /** * Sets value from given ID; if it was not found, sets the * first available entry as selectedValue @@ -85,16 +91,17 @@ export abstract class SelectFieldReference extends SelectField { } } if (! found) { - // default to first available entry if any - if (this._entries.length > 0) { - this.setValue(this._entries[0]); - } else { - // notify observers that no value is selected anymore - this.notifyObservers({ - "action": "select", - "value": undefined - }, this); - } + this.setDefaultValue(); + } + } + + protected setDefaultValue() { + // default to first available entry if any + if (this._entries.length > 0) { + this.setValue(this._entries[0]); + } else { + // notify observers that no value is selected anymore + this.notifySelectValueChanged(); } } } -- GitLab From 8682553e7bbbba56f9e94f6e7ec565d03228edea Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 22 Oct 2019 12:02:53 +0200 Subject: [PATCH 09/11] Fix bug in Solveur init --- .../formulaire/definition/concrete/form-solveur.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/formulaire/definition/concrete/form-solveur.ts b/src/app/formulaire/definition/concrete/form-solveur.ts index e472d035f..231f815b0 100644 --- a/src/app/formulaire/definition/concrete/form-solveur.ts +++ b/src/app/formulaire/definition/concrete/form-solveur.ts @@ -79,11 +79,13 @@ export class FormulaireSolveur extends FormulaireBase { if (sender instanceof SelectFieldParameter) { if (data.action === "select") { // update Solveur property: searched Parameter - const p: ParamDefinition = data.value.value; - this._currentNub.properties.setPropValue( - "searchedParameter", - p.nubUid + "/" + p.symbol - ); + try { + const p: ParamDefinition = data.value.value; + this._currentNub.properties.setPropValue( + "searchedParameter", + p.nubUid + "/" + p.symbol + ); + } catch (e) { } // reflect changes in GUI const inputXinit = this.getFormulaireNodeById("Xinit") as NgParameter; inputXinit.notifyValueModified(this); -- GitLab From 160f906368f403659070c9235359de34ef2d0d9b Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 22 Oct 2019 12:11:15 +0200 Subject: [PATCH 10/11] Updated e2e for Solveur compliance --- e2e/calculate-all-params.e2e-spec.ts | 7 ++++++- e2e/check-translations.e2e-spec.ts | 15 ++++++++------- e2e/clone-all-calc.e2e-spec.ts | 7 ++++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/e2e/calculate-all-params.e2e-spec.ts b/e2e/calculate-all-params.e2e-spec.ts index 6f5632baf..9533c2fca 100644 --- a/e2e/calculate-all-params.e2e-spec.ts +++ b/e2e/calculate-all-params.e2e-spec.ts @@ -18,7 +18,12 @@ describe("ngHyd − calculate all parameters of all calculators", () => { }); // get calculators list (IDs) @TODO read it from config, but can't import jalhyd here :/ - const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21 ]; + const calcTypes = [ + 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, + 11, 12, 13, 15, 17, 18, 19, 20, + 21, + // 22 - Solveur is not calculated here because it is not independent + ]; // for each calculator for (const ct of calcTypes) { diff --git a/e2e/check-translations.e2e-spec.ts b/e2e/check-translations.e2e-spec.ts index 2aef4ff34..b0c10f970 100644 --- a/e2e/check-translations.e2e-spec.ts +++ b/e2e/check-translations.e2e-spec.ts @@ -25,7 +25,7 @@ describe("ngHyd − check translation of all calculators", () => { }); // get calculators list (IDs) @TODO read it from config, but can't import jalhyd here :/ - const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21 ]; + const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22 ]; // options of "Language" selector on preferences page const langs = [ "English", "Français" ]; @@ -67,12 +67,13 @@ describe("ngHyd − check translation of all calculators", () => { // check that "compute" button is active const calcButton = calcPage.getCalculateButton(); const disabledState = await calcButton.getAttribute("disabled"); - expect(disabledState).not.toBe("true"); - // click "compute" button - await calcButton.click(); - // check that result is not empty - const hasResults = await calcPage.hasResults(); - expect(hasResults).toBe(true); + if (! disabledState) { + // click "compute" button + await calcButton.click(); + // check that result is not empty + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(true); + } // check absence of "*** message not found" in whole DOM expect(await browser.getPageSource()).not.toContain("*** message not found"); diff --git a/e2e/clone-all-calc.e2e-spec.ts b/e2e/clone-all-calc.e2e-spec.ts index e5b77dcd1..8c32c1ff6 100644 --- a/e2e/clone-all-calc.e2e-spec.ts +++ b/e2e/clone-all-calc.e2e-spec.ts @@ -18,7 +18,12 @@ describe("ngHyd − clone all calculators with all possible <select> values", () }); // get calculators list (IDs) @TODO read it from config, but can't import jalhyd here :/ - const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21 ]; + const calcTypes = [ + 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, + 11, 12, 13, 15, 17, 18, 19, 20, + 21, + // 22 - Solveur is not cloned here because it is not independent + ]; // for each calculator for (const ct of calcTypes) { -- GitLab From dea8594cc81d33396855e90a796b0c1d159a7eec Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Tue, 22 Oct 2019 14:56:09 +0200 Subject: [PATCH 11/11] Add e2e for Solveur --- e2e/session/session-solveur-chutes.json | 124 ++++++++++++++++++++++ e2e/solveur.e2e-spec.ts | 135 ++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 e2e/session/session-solveur-chutes.json create mode 100644 e2e/solveur.e2e-spec.ts diff --git a/e2e/session/session-solveur-chutes.json b/e2e/session/session-solveur-chutes.json new file mode 100644 index 000000000..3142108ec --- /dev/null +++ b/e2e/session/session-solveur-chutes.json @@ -0,0 +1,124 @@ +{ + "header": { + "source": "jalhyd", + "format_version": "1.3", + "created": "2019-10-22T08:08:13.816Z" + }, + "settings": { + "precision": 0.0001, + "maxIterations": 100, + "displayPrecision": 3 + }, + "documentation": "", + "session": [ + { + "uid": "NjRvcG", + "props": { + "calcType": "PabChute" + }, + "meta": { + "title": "PAB : chute" + }, + "children": [], + "parameters": [ + { + "symbol": "Z1", + "mode": "SINGLE", + "value": 2.1513761467889907 + }, + { + "symbol": "Z2", + "mode": "SINGLE", + "value": 0.8669724770642202 + }, + { + "symbol": "DH", + "mode": "CALCUL" + } + ] + }, + { + "uid": "dnV4bD", + "props": { + "calcType": "PabNombre" + }, + "meta": { + "title": "PAB : nombre" + }, + "children": [], + "parameters": [ + { + "symbol": "DHT", + "mode": "LINK", + "targetNub": "NjRvcG", + "targetParam": "DH" + }, + { + "symbol": "N", + "mode": "SINGLE", + "value": 10 + }, + { + "symbol": "DH", + "mode": "CALCUL" + } + ] + }, + { + "uid": "OHBpcz", + "props": { + "calcType": "PabPuissance" + }, + "meta": { + "title": "PAB : puissance" + }, + "children": [], + "parameters": [ + { + "symbol": "DH", + "mode": "LINK", + "targetNub": "dnV4bD", + "targetParam": "DH" + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 0.1 + }, + { + "symbol": "V", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "PV", + "mode": "CALCUL" + } + ] + }, + { + "uid": "ODM0Z2", + "props": { + "calcType": "Solveur", + "nubToCalculate": "OHBpcz", + "searchedParameter": "NjRvcG/Z2" + }, + "meta": { + "title": "Solveur" + }, + "children": [], + "parameters": [ + { + "symbol": "Xinit", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "Ytarget", + "mode": "SINGLE", + "value": 252 + } + ] + } + ] +} \ No newline at end of file diff --git a/e2e/solveur.e2e-spec.ts b/e2e/solveur.e2e-spec.ts new file mode 100644 index 000000000..84b3df0e9 --- /dev/null +++ b/e2e/solveur.e2e-spec.ts @@ -0,0 +1,135 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { browser } from "protractor"; +import { SideNav } from "./sidenav.po"; + +/** + * Clone calculators + */ +describe("Solveur - ", () => { + let startPage: AppPage; + let listPage: ListPage; + let calcPage: CalculatorPage; + let navbar: Navbar; + let sidenav: SideNav; + + beforeEach(() => { + startPage = new AppPage(); + listPage = new ListPage(); + calcPage = new CalculatorPage(); + navbar = new Navbar(); + sidenav = new SideNav(); + }); + + it("load > calculate", async () => { + await startPage.navigateTo(); + + await navbar.clickMenuButton(); + await browser.sleep(200); + + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + + await sidenav.loadSessionFile("./session/session-solveur-chutes.json"); + await browser.sleep(200); + + expect(await navbar.getAllCalculatorTabs().count()).toBe(4); + await navbar.clickCalculatorTab(3); // n°3 should be the latest + + // check input values + expect(await calcPage.getInputById("Xinit").getAttribute("value")).toBe("0.5"); + expect(await calcPage.getInputById("Ytarget").getAttribute("value")).toBe("252"); + // check Nub to calculate + const ntc = calcPage.getSelectById("select_target_nub"); + const ntcV = await calcPage.getSelectValueText(ntc); + expect(ntcV).toContain("PAB : puissance / Puissance dissipée (PV)"); + // check searched Parameter + const sp = calcPage.getSelectById("select_searched_param"); + const spV = await calcPage.getSelectValueText(sp); + expect(spV).toContain("Z2 - Cote aval (PAB : chute)"); + + // check that "compute" button is active + const calcButton = calcPage.getCalculateButton(); + const disabledState = await calcButton.getAttribute("disabled"); + expect(disabledState).not.toBe("true"); + // click "compute" button + await calcButton.click(); + // check that result is not empty + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(true); + }); + + it("create > feed > calculate > clone > calculate clone", async () => { + await startPage.navigateTo(); + + // 1. create empty Solveur + await listPage.clickMenuEntryForCalcType(22); // Solveur + await browser.sleep(500); + + // 2. create PAB:Chute, PAB:Nombre and PAB:Puissance linked to one another + await navbar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(12); // PAB:Chute + await browser.sleep(500); + await navbar.clickNewCalculatorButton(); + + await listPage.clickMenuEntryForCalcType(13); // PAB:Nombre + await browser.sleep(500); + // link DHT to PAB:Chute.DH + const dht = calcPage.getInputById("DHT"); + await calcPage.setParamMode(dht, "link"); + // Calculate DH + const dh_nombre = calcPage.getInputById("DH"); + await calcPage.setParamMode(dh_nombre, "cal"); + + await navbar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(6); // PAB:Puissance + await browser.sleep(500); + // link DH to PAB:Nombre.DH + const dh_puiss = calcPage.getInputById("DH"); + await calcPage.setParamMode(dh_puiss, "link"); + + // Go back to Solveur + await navbar.clickCalculatorTab(0); + + await calcPage.changeSelectValue(calcPage.getSelectById("select_target_nub"), 1); // "Puissance / PV" + await browser.sleep(500); + await calcPage.changeSelectValue(calcPage.getSelectById("select_searched_param"), 2); // "Chute / Z2" + await browser.sleep(500); + await calcPage.getInputById("Ytarget").sendKeys("318"); + + // check that "compute" button is active + const calcButton = calcPage.getCalculateButton(); + const disabledState = await calcButton.getAttribute("disabled"); + expect(disabledState).not.toBe("true"); + // click "compute" button + await calcButton.click(); + // check that result is not empty + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(true); + + // otherwise clickCloneCalcButton() fails with "Element is not clickable at point" + await browser.executeScript("window.scrollTo(0, 0);"); + await calcPage.clickCloneCalcButton(); + await browser.sleep(500); + + // 4. check existence of the cloned module + expect(await navbar.getAllCalculatorTabs().count()).toBe(5); + await navbar.clickCalculatorTab(4); // n°4 should be the latest + + // check that result is empty + const hasResultsClone1 = await calcPage.hasResults(); + expect(hasResultsClone1).toBe(false); + + // check that "compute" button is active + const calcButtonClone = calcPage.getCalculateButton(); + const disabledStateClone = await calcButtonClone.getAttribute("disabled"); + expect(disabledStateClone).not.toBe("true"); + // click "compute" button + await calcButtonClone.click(); + // check that result is not empty + const hasResultsClone2 = await calcPage.hasResults(); + expect(hasResultsClone2).toBe(true); + }); +}); -- GitLab