diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts index d4b7a51d5adc0649cc0a73b1fdb2a04ce2ff1a7b..9d05454ed7dc9e1d90855850777fa2d4b79e76f6 100644 --- a/e2e/calculator.po.ts +++ b/e2e/calculator.po.ts @@ -1,4 +1,4 @@ -import { browser, by, element } from "protractor"; +import { by, element, ElementFinder, browser } from "protractor"; export class CalculatorPage { @@ -9,4 +9,66 @@ export class CalculatorPage { getHeader1() { return element(by.css("h1")); } + + getSelectById(id: string) { + return element(by.css("mat-select#" + id)); + } + + getInputById(id: string) { + return element(by.css("input#" + id)); + } + + getSaveSessionButton() { + return element(by.css("dialog-save-session button[type=submit]")); + } + + scrollTo(elt: ElementFinder) { + browser.controlFlow().execute(function() { + browser.executeScript("arguments[0].scrollIntoView(true)", elt.getWebElement()); + }); + } + + async clickSaveCalcButton() { + return await element(by.css("#save-calc")).click(); + } + + async clickCloneCalcButton() { + const cloneButton = element(by.css("#clone-calc")); + return await cloneButton.click(); + } + + async changeSelectValue(elt: ElementFinder, index: number) { + await elt.click(); + const options = (await elt.getAttribute("aria-owns")).split(" "); + const optId = options[index]; + const option = element(by.id(optId)); + await option.click(); + } + + // find parent element of elt having class "container" + async findParentContainer(elt: ElementFinder): Promise<ElementFinder> { + let i = 8; // garde fous + while ((await elt.getAttribute("class") !== "container") && (i >= 0)) { + elt = elt.element(by.xpath("..")); + i--; + } + return elt; + } + + /** + * @param elt an <input> element + * @param mode "fix", "var", "cal" or "link" + */ + async setParamMode(elt: ElementFinder, mode: string) { + // get parent (div.container) + const container = await this.findParentContainer(elt); + // find radio buttons + const button = container.element(by.css("button#radio_" + mode + "-button")); + await button.click(); + // for "var" mode, close the modal + if (mode === "var") { + await browser.sleep(500); + await element(by.css("dialog-edit-param-values .mat-dialog-actions button")).click(); + } + } } diff --git a/e2e/clone-calc.e2e-spec.ts b/e2e/clone-calc.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..21b6206f784d2e219f8ab845a1437a26c73a3570 --- /dev/null +++ b/e2e/clone-calc.e2e-spec.ts @@ -0,0 +1,68 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { SideNav } from "./sidenav.po"; +import { browser } from "protractor"; + +describe("ngHyd − clone a calculator", () => { + 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("when cloning a calculator, the clone should have the same values for all parameters", async () => { + await startPage.navigateTo(); + + // 1. create target modules for linked parameter + await listPage.clickMenuEntryForCalcType(3); // Régime uniforme + await browser.sleep(500); + const debitRU = calcPage.getInputById("calc_Q"); // "Débit" is calculated by default + await calcPage.setParamMode(debitRU, "fix"); + await browser.sleep(500); + + await navbar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(4); // Courbe de remous + await browser.sleep(500); + + // 2. create source module to clone + await navbar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); // Section paramétrée + await browser.sleep(500); + + // 3. change and store source parameter values + const sourceValues = { + k: 0.6, + Ks: 42 + }; + // await calcPage.changeSelectValue(calcPage.getSelectById("select_section"), 3); // mode "parabolique" + // await calcPage.getInputById("k").clear(); + // await calcPage.getInputById("k").sendKeys(sourceValues["k"]); + await calcPage.getInputById("Ks").clear(); + await calcPage.getInputById("Ks").sendKeys(sourceValues["Ks"]); + // link "Débit" to "Courbe de remous" + const debitSP = calcPage.getInputById("Q"); + await calcPage.setParamMode(debitSP, "link"); + await browser.sleep(500); + await calcPage.changeSelectValue(calcPage.getSelectById("linked_Q"), 1); // "Courbe de remous" + await browser.sleep(500); + + // 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 the cloned module + expect(await navbar.getAllCalculatorTabs().count()).toBe(4); + await navbar.clickCalculatorTab(3); // n°3 should be the latest + + }); +}); diff --git a/e2e/list.po.ts b/e2e/list.po.ts index 2f06248a15933ff12b610e0870991d8d4cfe39dc..4e32624df6e5941c5b92c91bcf08d71044e4bf24 100644 --- a/e2e/list.po.ts +++ b/e2e/list.po.ts @@ -27,4 +27,9 @@ export class ListPage { const r = Math.min((Math.floor(Math.random() * l)), (l - 1)); return menuEntries.get(r).click(); } + + async clickMenuEntryForCalcType(type: number) { + const but = element(by.css("#create-calc-" + type)); + return but.click(); + } } diff --git a/e2e/load-save-session.e2e-spec.ts b/e2e/load-save-session.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b288a375f9e67fb0f83f6f2a90386fae3d058624 --- /dev/null +++ b/e2e/load-save-session.e2e-spec.ts @@ -0,0 +1,86 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { SideNav } from "./sidenav.po"; +import { browser } from "protractor"; + +describe("ngHyd − save and load sessions", () => { + 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("when loading session-6-calc.test.json file from home page, 6 calculators should be loaded", async () => { + await startPage.navigateTo(); + + await navbar.clickMenuButton(); + await browser.sleep(200); + + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + + await sidenav.loadSessionFile("./session-6-calc.test.json"); + await browser.sleep(200); + + expect(await navbar.getAllCalculatorTabs().count()).toBe(6); + }); + + it("when loading session-optional-params.test.json file from home page, the calculator should be loaded", async () => { + await startPage.navigateTo(); + + await navbar.clickMenuButton(); + await browser.sleep(200); + + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + + await sidenav.loadSessionFile("./session-optional-params.test.json"); + await browser.sleep(200); + + expect(await navbar.getAllCalculatorTabs().count()).toBe(1); + }); + + it("when saving a calculator, the current parameter values should be found in the file", async () => { + await startPage.navigateTo(); + + await listPage.clickMenuEntryForCalcType(2); // Section paramétrée + await browser.sleep(500); + + await calcPage.changeSelectValue(calcPage.getSelectById("select_section"), 2); // mode "circulaire" + + await calcPage.getInputById("Ks").clear(); // coefficient de Strickler + await calcPage.getInputById("Ks").sendKeys("42"); + + await calcPage.clickSaveCalcButton(); + + // see: https://stackoverflow.com/questions/21935696/protractor-e2e-test-case-for-downloading-pdf-file + const fs = require("fs"); + const path = require("path"); + const os = require("os"); + const filename = path.resolve(os.homedir(), "Téléchargements/session.json"); + if (fs.existsSync(filename)) { + // Make sure the browser doesn't have to rename the download. + fs.unlinkSync(filename); + } + + await calcPage.getSaveSessionButton().click(); + await browser.sleep(1000); + const fileContent = fs.readFileSync(filename, { encoding: "utf8" }); + + // tslint:disable-next-line:quotemark + expect(fileContent).toContain('{"id":"select_section","selected_id":"select_section_circ"}'); + // tslint:disable-next-line:quotemark + expect(fileContent).toContain('{"param":{"id":"Ks","values":{"mode":"SINGLE","value":42}}}'); + }); + +}); diff --git a/e2e/load-sesssion.e2e-spec.ts b/e2e/load-sesssion.e2e-spec.ts deleted file mode 100644 index 39f919a41a020378ddc0b05f33fc0d179d3c6cbd..0000000000000000000000000000000000000000 --- a/e2e/load-sesssion.e2e-spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AppPage } from "./app.po"; -import { Navbar } from "./navbar.po"; -import { SideNav } from "./sidenav.po"; -import { browser } from "protractor"; - -describe("ngHyd − start page", () => { - let page: AppPage; - let navbar: Navbar; - let sidenav: SideNav; - - beforeEach(() => { - page = new AppPage(); - navbar = new Navbar(); - sidenav = new SideNav(); - }); - - it("when loading session-6-calc.test.json file from home page, 6 calculators should be loaded", async () => { - await page.navigateTo(); - - await navbar.clickMenuButton(); - await browser.sleep(200); - - await sidenav.clickLoadSessionButton(); - await browser.sleep(200); - - await sidenav.loadSessionFile("./session-6-calc.test.json"); - await browser.sleep(200); - - expect(await navbar.getAllCalculatorTabs().count()).toBe(6); - }); - - /*it("when app starts, user should see the list of available compute nodes", () => { - page.navigateTo(); - expect(page.getListLength()).toBeGreaterThan(8); - });*/ -}); diff --git a/e2e/session-6-calc.test.json b/e2e/session-6-calc.test.json index 070c2019ffbbcf04af8916c80df6de9adf073391..4dab5ec3d21e82dee47b8e81bc24a04328e71317 100644 --- a/e2e/session-6-calc.test.json +++ b/e2e/session-6-calc.test.json @@ -1 +1,811 @@ -{"session":{"elements":[{"form":{"id":"Conduite distributrice (MTJmNH)","uid":"MTJmNH","props":{"calcType":0,"nodeType":0},"elements":[{"fieldset":{"id":"fs_hydraulique","props":{"calcType":0,"nodeType":0},"elements":[{"param":{"id":"Q","values":{"mode":"SINGLE","value":3}}},{"param":{"id":"D","values":{"mode":"SINGLE","value":1.2}}},{"param":{"id":"J","values":{"mode":"CALCUL"}}},{"param":{"id":"Lg","values":{"mode":"SINGLE","value":100}}},{"param":{"id":"Nu","values":{"mode":"SINGLE","value":0.000001}}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":0,"nodeType":0},"elements":[{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}}]}}]}},{"form":{"id":"Lechapt-Calmon (NHdtdT)","uid":"NHdtdT","props":{"calcType":1,"nodeType":0},"elements":[{"fieldset":{"id":"fs_materiau","props":{"calcType":1,"nodeType":0},"elements":[{"select":{"id":"select_material","selected_id":"select_material_1"}},{"param":{"id":"L","values":{"mode":"SINGLE","value":1.863}}},{"param":{"id":"M","values":{"mode":"SINGLE","value":2}}},{"param":{"id":"N","values":{"mode":"SINGLE","value":5.33}}}]}},{"fieldset":{"id":"fs_hydraulique","props":{"calcType":1,"nodeType":0},"elements":[{"param":{"id":"Q","values":{"mode":"SINGLE","value":3}}},{"param":{"id":"D","values":{"mode":"SINGLE","value":1.2}}},{"param":{"id":"J","values":{"mode":"CALCUL"}}},{"param":{"id":"Lg","values":{"mode":"SINGLE","value":100}}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":1,"nodeType":0},"elements":[{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}}]}}]}},{"form":{"id":"Section paramétrée (YjZxc2)","uid":"YjZxc2","props":{"calcType":2,"nodeType":2},"elements":[{"fieldset":{"id":"fs_section","props":{"calcType":2,"nodeType":2},"elements":[{"select":{"id":"select_section","selected_id":"select_section_rect"}},{"param":{"id":"LargeurBerge","values":{"mode":"SINGLE","value":2.5}}}]}},{"fieldset":{"id":"fs_bief","props":{"calcType":2,"nodeType":2},"elements":[{"param":{"id":"Ks","values":{"mode":"SINGLE","value":40}}},{"param":{"id":"If","values":{"mode":"SINGLE","value":0.001}}},{"param":{"id":"YB","values":{"mode":"SINGLE","value":1}}}]}},{"fieldset":{"id":"fs_hydraulique","props":{"calcType":2,"nodeType":2},"elements":[{"param":{"id":"Q","values":{"mode":"SINGLE","value":1.2}}},{"param":{"id":"Y","values":{"mode":"SINGLE","value":0.8}}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":2,"nodeType":2},"elements":[{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}}]}},{"fieldset":{"id":"fs_computed_var","props":{"calcType":2,"nodeType":2},"elements":[{"select":{"id":"select_target","selected_id":"select_target_Hs"}}]}}]}},{"form":{"id":"Régime uniforme (ZmEwcX)","uid":"ZmEwcX","props":{"calcType":3,"nodeType":2},"elements":[{"fieldset":{"id":"fs_section","props":{"calcType":3,"nodeType":2},"elements":[{"select":{"id":"select_section","selected_id":"select_section_rect"}},{"param":{"id":"LargeurBerge","values":{"mode":"SINGLE","value":2.5}}}]}},{"fieldset":{"id":"fs_bief","props":{"calcType":3,"nodeType":2},"elements":[{"param":{"id":"Ks","values":{"mode":"SINGLE","value":40}}},{"param":{"id":"If","values":{"mode":"SINGLE","value":0.001}}},{"param":{"id":"YB","values":{"mode":"SINGLE","value":1}}}]}},{"fieldset":{"id":"fs_hydraulique","props":{"calcType":3,"nodeType":2},"elements":[{"param":{"id":"Q","values":{"mode":"CALCUL"}}},{"param":{"id":"Y","values":{"mode":"SINGLE","value":0.8}}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":3,"nodeType":2},"elements":[{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}}]}}]}},{"form":{"id":"Courbes de remous (NHdmeG)","uid":"NHdmeG","props":{"calcType":4,"nodeType":2},"elements":[{"fieldset":{"id":"fs_section","props":{"calcType":4,"nodeType":2},"elements":[{"select":{"id":"select_section","selected_id":"select_section_rect"}},{"param":{"id":"LargeurBerge","values":{"mode":"SINGLE","value":2.5}}}]}},{"fieldset":{"id":"fs_bief","props":{"calcType":4,"nodeType":2},"elements":[{"param":{"id":"Ks","values":{"mode":"SINGLE","value":40}}},{"param":{"id":"Long","values":{"mode":"SINGLE","value":100}}},{"param":{"id":"If","values":{"mode":"SINGLE","value":0.001}}},{"param":{"id":"YB","values":{"mode":"SINGLE","value":1}}}]}},{"fieldset":{"id":"fs_condlim","props":{"calcType":4,"nodeType":2},"elements":[{"param":{"id":"Q","values":{"mode":"SINGLE","value":1.2}}},{"param":{"id":"Yaval","values":{"mode":"SINGLE","value":0.4}}},{"param":{"id":"Yamont","values":{"mode":"SINGLE","value":0.15}}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":4,"nodeType":2},"elements":[{"param":{"id":"Dx","values":{"mode":"SINGLE","value":5}}},{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}},{"select":{"id":"select_resolution","selected_id":"select_resolution_trap"}}]}},{"fieldset":{"id":"fs_target_data","props":{"calcType":4,"nodeType":2},"elements":[{"select":{"id":"select_target","selected_id":"select_target_none"}}]}}]}},{"form":{"id":"Lois d'ouvrages (Yzgxan)","uid":"Yzgxan","props":{"calcType":8,"nodeType":0},"elements":[{"fieldset":{"id":"fs_param_hydro","props":{"calcType":8,"nodeType":0},"elements":[{"param":{"id":"Q","values":{"mode":"CALCUL"}}},{"param":{"id":"Z1","values":{"mode":"SINGLE","value":102}}},{"param":{"id":"Z2","values":{"mode":"SINGLE","value":101.5}}}]}},{"fieldset_container":{"id":"struct_container","elements":[{"fieldset":{"id":"fs_ouvrage","props":{"calcType":7,"nodeType":5,"structureType":1,"loiDebit":1},"elements":[{"select":{"id":"select_ouvrage","selected_id":"select_ouvrage_vanne_rect"}},{"select":{"id":"select_loidebit1","selected_id":"select_loidebit1_cem88d"}},{"select":{"id":"select_loidebit2","selected_id":"select_loidebit2_cem88v"}},{"select":{"id":"select_loidebit3","selected_id":"select_loidebit3_seuiltriang"}},{"select":{"id":"select_loidebit4","selected_id":"select_loidebit4_seuiltriangtrunc"}},{"param":{"id":"ZDV","values":{"mode":"SINGLE","value":100}}},{"param":{"id":"L","values":{"mode":"SINGLE","value":2}}},{"param":{"id":"W","values":{"mode":"SINGLE","value":null}}},{"param":{"id":"Cd","values":{"mode":"SINGLE","value":0.4}}}]}}]}},{"fieldset":{"id":"fs_param_calc","props":{"calcType":8,"nodeType":0},"elements":[{"param":{"id":"Pr","values":{"mode":"SINGLE","value":0.0001}}}]}}]}}]}} \ No newline at end of file +{ + "session": { + "elements": [ + { + "form": { + "id": "Conduite distributrice (MTJmNH)", + "uid": "MTJmNH", + "props": { + "calcType": 0, + "nodeType": 0 + }, + "elements": [ + { + "fieldset": { + "id": "fs_hydraulique", + "props": { + "calcType": 0, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "SINGLE", + "value": 3 + } + } + }, + { + "param": { + "id": "D", + "values": { + "mode": "SINGLE", + "value": 1.2 + } + } + }, + { + "param": { + "id": "J", + "values": { + "mode": "CALCUL" + } + } + }, + { + "param": { + "id": "Lg", + "values": { + "mode": "SINGLE", + "value": 100 + } + } + }, + { + "param": { + "id": "Nu", + "values": { + "mode": "SINGLE", + "value": 0.000001 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 0, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + } + ] + } + }, + { + "form": { + "id": "Lechapt-Calmon (NHdtdT)", + "uid": "NHdtdT", + "props": { + "calcType": 1, + "nodeType": 0 + }, + "elements": [ + { + "fieldset": { + "id": "fs_materiau", + "props": { + "calcType": 1, + "nodeType": 0 + }, + "elements": [ + { + "select": { + "id": "select_material", + "selected_id": "select_material_1" + } + }, + { + "param": { + "id": "L", + "values": { + "mode": "SINGLE", + "value": 1.863 + } + } + }, + { + "param": { + "id": "M", + "values": { + "mode": "SINGLE", + "value": 2 + } + } + }, + { + "param": { + "id": "N", + "values": { + "mode": "SINGLE", + "value": 5.33 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_hydraulique", + "props": { + "calcType": 1, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "SINGLE", + "value": 3 + } + } + }, + { + "param": { + "id": "D", + "values": { + "mode": "SINGLE", + "value": 1.2 + } + } + }, + { + "param": { + "id": "J", + "values": { + "mode": "CALCUL" + } + } + }, + { + "param": { + "id": "Lg", + "values": { + "mode": "SINGLE", + "value": 100 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 1, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + } + ] + } + }, + { + "form": { + "id": "Section paramétrée (YjZxc2)", + "uid": "YjZxc2", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "fieldset": { + "id": "fs_section", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "select": { + "id": "select_section", + "selected_id": "select_section_rect" + } + }, + { + "param": { + "id": "LargeurBerge", + "values": { + "mode": "SINGLE", + "value": 2.5 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_bief", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Ks", + "values": { + "mode": "SINGLE", + "value": 40 + } + } + }, + { + "param": { + "id": "If", + "values": { + "mode": "SINGLE", + "value": 0.001 + } + } + }, + { + "param": { + "id": "YB", + "values": { + "mode": "SINGLE", + "value": 1 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_hydraulique", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "SINGLE", + "value": 1.2 + } + } + }, + { + "param": { + "id": "Y", + "values": { + "mode": "SINGLE", + "value": 0.8 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_computed_var", + "props": { + "calcType": 2, + "nodeType": 2 + }, + "elements": [ + { + "select": { + "id": "select_target", + "selected_id": "select_target_Hs" + } + } + ] + } + } + ] + } + }, + { + "form": { + "id": "Régime uniforme (ZmEwcX)", + "uid": "ZmEwcX", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "elements": [ + { + "fieldset": { + "id": "fs_section", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "elements": [ + { + "select": { + "id": "select_section", + "selected_id": "select_section_rect" + } + }, + { + "param": { + "id": "LargeurBerge", + "values": { + "mode": "SINGLE", + "value": 2.5 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_bief", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Ks", + "values": { + "mode": "SINGLE", + "value": 40 + } + } + }, + { + "param": { + "id": "If", + "values": { + "mode": "SINGLE", + "value": 0.001 + } + } + }, + { + "param": { + "id": "YB", + "values": { + "mode": "SINGLE", + "value": 1 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_hydraulique", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "CALCUL" + } + } + }, + { + "param": { + "id": "Y", + "values": { + "mode": "SINGLE", + "value": 0.8 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + } + ] + } + }, + { + "form": { + "id": "Courbes de remous (NHdmeG)", + "uid": "NHdmeG", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "fieldset": { + "id": "fs_section", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "select": { + "id": "select_section", + "selected_id": "select_section_rect" + } + }, + { + "param": { + "id": "LargeurBerge", + "values": { + "mode": "SINGLE", + "value": 2.5 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_bief", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Ks", + "values": { + "mode": "SINGLE", + "value": 40 + } + } + }, + { + "param": { + "id": "Long", + "values": { + "mode": "SINGLE", + "value": 100 + } + } + }, + { + "param": { + "id": "If", + "values": { + "mode": "SINGLE", + "value": 0.001 + } + } + }, + { + "param": { + "id": "YB", + "values": { + "mode": "SINGLE", + "value": 1 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_condlim", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "SINGLE", + "value": 1.2 + } + } + }, + { + "param": { + "id": "Yaval", + "values": { + "mode": "SINGLE", + "value": 0.4 + } + } + }, + { + "param": { + "id": "Yamont", + "values": { + "mode": "SINGLE", + "value": 0.15 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "param": { + "id": "Dx", + "values": { + "mode": "SINGLE", + "value": 5 + } + } + }, + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + }, + { + "select": { + "id": "select_resolution", + "selected_id": "select_resolution_trap" + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_target_data", + "props": { + "calcType": 4, + "nodeType": 2 + }, + "elements": [ + { + "select": { + "id": "select_target", + "selected_id": "select_target_none" + } + } + ] + } + } + ] + } + }, + { + "form": { + "id": "Lois d'ouvrages (Yzgxan)", + "uid": "Yzgxan", + "props": { + "calcType": 8, + "nodeType": 0 + }, + "elements": [ + { + "fieldset": { + "id": "fs_param_hydro", + "props": { + "calcType": 8, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "CALCUL" + } + } + }, + { + "param": { + "id": "Z1", + "values": { + "mode": "SINGLE", + "value": 102 + } + } + }, + { + "param": { + "id": "Z2", + "values": { + "mode": "SINGLE", + "value": 101.5 + } + } + } + ] + } + }, + { + "fieldset_container": { + "id": "struct_container", + "elements": [ + { + "fieldset": { + "id": "fs_ouvrage", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 1, + "loiDebit": 1 + }, + "elements": [ + { + "select": { + "id": "select_ouvrage", + "selected_id": "select_ouvrage_vanne_rect" + } + }, + { + "select": { + "id": "select_loidebit1", + "selected_id": "select_loidebit1_cem88d" + } + }, + { + "select": { + "id": "select_loidebit2", + "selected_id": "select_loidebit2_cem88v" + } + }, + { + "select": { + "id": "select_loidebit3", + "selected_id": "select_loidebit3_seuiltriang" + } + }, + { + "select": { + "id": "select_loidebit4", + "selected_id": "select_loidebit4_seuiltriangtrunc" + } + }, + { + "param": { + "id": "ZDV", + "values": { + "mode": "SINGLE", + "value": 100 + } + } + }, + { + "param": { + "id": "L", + "values": { + "mode": "SINGLE", + "value": 2 + } + } + }, + { + "param": { + "id": "W", + "values": { + "mode": "SINGLE", + "value": null + } + } + }, + { + "param": { + "id": "Cd", + "values": { + "mode": "SINGLE", + "value": 0.4 + } + } + } + ] + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 8, + "nodeType": 0 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/e2e/session-optional-params.test.json b/e2e/session-optional-params.test.json new file mode 100644 index 0000000000000000000000000000000000000000..0d5cea59ec6fed688895456e8abe90f0e8a53188 --- /dev/null +++ b/e2e/session-optional-params.test.json @@ -0,0 +1,160 @@ +{ + "session": { + "elements": [ + { + "form": { + "id": "Section paramétrée", + "uid": "ZDZ1Yn", + "props": { + "calcType": 2, + "nodeType": 4 + }, + "elements": [ + { + "fieldset": { + "id": "fs_section", + "props": { + "calcType": 2, + "nodeType": 4 + }, + "elements": [ + { + "select": { + "id": "select_section", + "selected_id": "select_section_puiss" + } + }, + { + "param": { + "id": "k", + "values": { + "mode": "SINGLE", + "value": 0.5 + } + } + }, + { + "param": { + "id": "LargeurBerge", + "values": { + "mode": "SINGLE", + "value": 4 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_bief", + "props": { + "calcType": 2, + "nodeType": 4 + }, + "elements": [ + { + "param": { + "id": "Ks", + "values": { + "mode": "SINGLE", + "value": 40 + } + } + }, + { + "param": { + "id": "If", + "values": { + "mode": "MINMAX", + "min": 0.0005, + "max": 0.002, + "step": 0.00007500000000000001 + } + } + }, + { + "param": { + "id": "YB", + "values": { + "mode": "SINGLE", + "value": 1 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_hydraulique", + "props": { + "calcType": 2, + "nodeType": 4 + }, + "elements": [ + { + "param": { + "id": "Q", + "values": { + "mode": "SINGLE", + "value": 1.2 + } + } + }, + { + "param": { + "id": "Y", + "values": { + "mode": "SINGLE", + "value": 0.8 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_param_calc", + "props": { + "calcType": 2, + "nodeType": 4 + }, + "elements": [ + { + "param": { + "id": "Pr", + "values": { + "mode": "SINGLE", + "value": 0.0001 + } + } + } + ] + } + }, + { + "fieldset": { + "id": "fs_computed_var", + "props": { + "calcType": 2, + "nodeType": 4, + "varCalc": "B" + }, + "elements": [ + { + "select": { + "id": "select_target", + "selected_id": "select_target_B" + } + } + ] + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5a2713b26c5e5a1165ba32394037b4d218277c0b..bead599e1f868212b954745b7ec76f9f61afa37a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nghyd", - "version": "1.2.0", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bb1875351a7cd2f95737651be8ae0fd92da0f459..0ffdf2dc9269207bebb79a330a8348ed899f51a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nghyd", - "version": "4.0.0", + "version": "4.1.0", "license": "MIT", "scripts": { "ng": "ng", @@ -10,6 +10,7 @@ "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", + "e2equick": "ng e2e --dev-server-target= --baseUrl=http://localhost:4200", "jalhyd": "rm node_modules/jalhyd; cd ../jalhyd; npm run build; cd ../nghyd; npm install ../jalhyd;", "mathjax": "rsync -az --delete node_modules/mathjax docs-fr/javascripts;", "mkdocs": "npm run mathjax; find docs-fr/javascripts/ -name '*.md' -type f -delete; python3 -m mkdocs build", diff --git a/protractor.conf.js b/protractor.conf.js index a2584537bfd8d7b18361bfb3ace681021ba2d824..a4cc5bdbab60581670a5612667587e830a0aa818 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -9,7 +9,16 @@ exports.config = { './e2e/**/*.e2e-spec.ts' ], capabilities: { - 'browserName': 'chrome' + browserName: 'chrome', + chromeOoptions: { + prefs: { + download: { + prompt_for_download: false, + directory_upgrade: true, + default_directory: '/tmp/e2e-downloads' // @WARNING marche pas !? + }, + }, + }, }, directConnect: true, baseUrl: 'http://localhost:4201/', @@ -24,5 +33,6 @@ exports.config = { project: 'e2e/tsconfig.e2e.json' }); jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + browser.manage().window().setSize(1600, 1000); } }; diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts index f89955f565aacae16b9f9cf676fb607bf8a5c2ed..e04b0a4607e2d3632eab3e07700c7a8b2c0bb888 100644 --- a/src/app/components/base-param-input/base-param-input.component.ts +++ b/src/app/components/base-param-input/base-param-input.component.ts @@ -2,11 +2,12 @@ import { Component, ChangeDetectorRef } from "@angular/core"; -import { Message, ParamDefinition, ParamDomain, ParamDomainValue, Observable, isNumeric } from "jalhyd"; +import { Message, ParamDefinition, ParamDomain, ParamDomainValue, Observable } from "jalhyd"; import { I18nService } from "../../services/internationalisation/internationalisation.service"; import { GenericInputComponent } from "../generic-input/generic-input.component"; import { ServiceFactory } from "../../services/service-factory"; +import { NgParameter } from "../../formulaire/ngparam"; export class NgBaseParam extends Observable { private _param: ParamDefinition; @@ -98,8 +99,8 @@ export class BaseParamInputComponent extends GenericInputComponent { /** * paramètre géré */ - private get _paramDef(): NgBaseParam { - return this._model; + private get _paramDef(): NgParameter { + return this._model as NgParameter; } /** @@ -119,13 +120,15 @@ export class BaseParamInputComponent extends GenericInputComponent { protected setModelValue(sender: any, v: any) { this._tmp = v; try { - this._paramDef.setValue(v); + this._paramDef.setValue(null, v); } catch (e) { // géré par validateModelValue() } } protected validateModelValue(v: any): { isValid: boolean, message: string } { - return this._model.validateModelValue(v); + if (this._model instanceof NgBaseParam) { + return this._model.validateModelValue(v); + } } } diff --git a/src/app/components/calculator-list/calculator-list.component.html b/src/app/components/calculator-list/calculator-list.component.html index 925ebed1196278810f8b7be0598805d63c2730b2..25c88f0250120f157df3e2ea882db154a79ebfe2 100644 --- a/src/app/components/calculator-list/calculator-list.component.html +++ b/src/app/components/calculator-list/calculator-list.component.html @@ -24,7 +24,7 @@ <mat-card-actions> <div class="container" fxLayout="column" fxLayoutAlign="left" fxLayoutGap="10px"> <button mat-raised-button color="accent" *ngFor="let calc of theme.calculators" class="theme-calculator" - (click)="create(calc.type)" [innerHTML]="calc.label"></button> + (click)="create(calc.type)" [innerHTML]="calc.label" [id]="calc.buttonId"></button> </div> </mat-card-actions> diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts index aef92e3e136016e8bc40b94cc47ae1f8ee66c474..4a084aa81a7fe2c3b0b96d48a04cb393cd6e75a6 100644 --- a/src/app/components/calculator-list/calculator-list.component.ts +++ b/src/app/components/calculator-list/calculator-list.component.ts @@ -50,7 +50,8 @@ export class CalculatorListComponent implements OnInit { for (const calcType of theme.calculators) { item.calculators.push({ type: calcType, - label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(calcType) + label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(calcType), + buttonId: "create-calc-" + calcType }); // mark as used const index = unusedCalculators.indexOf(calcType); @@ -77,7 +78,8 @@ export class CalculatorListComponent implements OnInit { if (t !== CalculatorType.Structure) { unusedTheme.calculators.push({ type: t, - label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(t) + label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(t), + buttonId: "create-calc-" + t }); } } diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html index 9ce4c7853638fdcd3f2aa185b1d549f5bfb9dcd7..2720065f10a449408185a2f1e3d125a2495e1347 100644 --- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html @@ -88,7 +88,7 @@ <div fxHide.xs fxFlex.gt-xs="0 0 16px"></div> - <mat-form-field class="values-file" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%"> + <mat-form-field class="values-file file-input-field" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%"> <ngx-mat-file-input #valuesFile [placeholder]="uitextImportFile" (change)="onFileSelected($event)" formControlName="file"> </ngx-mat-file-input> diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog-load-session/dialog-load-session.component.html index b9f411fb767fdff20042560145f24d00edfef737..0473b4cdeb95ad579bf3a19d62aeb8fda50f2427 100644 --- a/src/app/components/dialog-load-session/dialog-load-session.component.html +++ b/src/app/components/dialog-load-session/dialog-load-session.component.html @@ -4,7 +4,7 @@ <div mat-dialog-content> - <mat-form-field> + <mat-form-field class="file-input-field"> <ngx-mat-file-input id="session-file-input" #sessionFile formControlName="file"[placeholder]="uitextLoadSessionFilename" (change)="onFileSelected($event)"></ngx-mat-file-input> <button mat-icon-button matSuffix *ngIf="!sessionFile.empty" (click)="sessionFile.clear($event)"> diff --git a/src/app/components/fixedvar-results/fixed-results.component.ts b/src/app/components/fixedvar-results/fixed-results.component.ts index cf23921d8e5e41b06fc6c2346245b324a70e171d..8207c30c1d9c35a2cf293fad04cbf52afcaa65d2 100644 --- a/src/app/components/fixedvar-results/fixed-results.component.ts +++ b/src/app/components/fixedvar-results/fixed-results.component.ts @@ -81,7 +81,7 @@ export class FixedResultsComponent { && res.extraResults ) { // 2.1. main result (sometimes empty, for ex. in "Section paramétrée") - if (res.name && res.resultElement.vCalc) { + if (res.name && res.resultElement.vCalc !== undefined) { data.push({ label: this._fixedResults.calculatedParameterHeader, value: this.intlService.formatResult(res.name, res.resultElement.vCalc), diff --git a/src/app/components/generic-calculator/calc-name.component.html b/src/app/components/generic-calculator/calc-name.component.html index 3866997a9293167d49281314dc156818649c9b17..85c1f9c069760458e4f52acd8d78e5ccc78d514c 100644 --- a/src/app/components/generic-calculator/calc-name.component.html +++ b/src/app/components/generic-calculator/calc-name.component.html @@ -1,5 +1,5 @@ <mat-form-field> <input matInput #inputControl="ngModel" class="form-control" type="text" - [id]="inputId" [(ngModel)]="uiValue" [placeholder]="title" required> + id="calc-name-input" [(ngModel)]="uiValue" [placeholder]="title" required> <mat-error>{{ errorMessage }}</mat-error> </mat-form-field> diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts index 70c236305fa3c4f667eb8fc1b8b95fb8914d65dc..208d11e9737a1484ac513de75271a67efc88c7fe 100644 --- a/src/app/components/generic-calculator/calc-name.component.ts +++ b/src/app/components/generic-calculator/calc-name.component.ts @@ -19,7 +19,7 @@ export class CalculatorNameComponent extends GenericInputComponent { * formulaire géré */ private get _form(): FormulaireDefinition { - return this._model; + return this._model as FormulaireDefinition; } /** diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html index 7874908f369790519d73e029d0819a560c0fb714..4c2623ff5d94eb7fc43befc6dd660c8dc4efcafc 100644 --- a/src/app/components/generic-calculator/calculator.component.html +++ b/src/app/components/generic-calculator/calculator.component.html @@ -1,14 +1,16 @@ -<mat-card id="calculator-card"> +<mat-card class="calculator-card" [id]="ID"> <mat-card-header> <div class="hyd-window-btns"> <!-- bouton d'aide --> <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon> + <!-- bouton de duplication --> + <mat-icon id="clone-calc" (click)="cloneCalculator()">file_copy</mat-icon> <!-- bouton de sauvegarde --> - <mat-icon (click)="saveCalculator()">save_alt</mat-icon> + <mat-icon id="save-calc" (click)="saveCalculator()">save_alt</mat-icon> <!-- bouton de fermeture --> - <mat-icon (click)="closeCalculator()">close</mat-icon> + <mat-icon id="close-calc" (click)="closeCalculator()">close</mat-icon> </div> <!-- titre --> diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss index 2346a487428bcb44594db9221dc7e84c2e6fd3ab..68df692b88e3ab7eeff62572d0de073c0db48587 100644 --- a/src/app/components/generic-calculator/calculator.component.scss +++ b/src/app/components/generic-calculator/calculator.component.scss @@ -24,7 +24,7 @@ mat-card { margin-bottom: 2em; // main card - &#calculator-card { + &.calculator-card { > mat-card-header { margin-bottom: .5em; diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index 3496192aabb2636a334ae6753ef68c73ea743936..e8c97032324028131f9dda6cb21bf7938681d244 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -87,6 +87,14 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, private intlService: I18nService; private formulaireService: FormulaireService; + public get ID() { + if (this._formulaire) { + return this._formulaire.uid; + } else { + return "calculator_1"; + } + } + constructor( private route: ActivatedRoute, private confirmCloseCalcDialog: MatDialog @@ -399,4 +407,9 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, } }); } + + public cloneCalculator() { + const serializedForm = this._formulaire.JSONserialise()["form"]; + this.formulaireService.deserialiseForm(serializedForm); + } } diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts index a7cd9064ce906ff59382e4dc0382751f45fd1073..4b34c81133b49c704c6fdb00f2427f0f1f47c1e4 100644 --- a/src/app/components/generic-input/generic-input.component.ts +++ b/src/app/components/generic-input/generic-input.component.ts @@ -2,6 +2,8 @@ import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, ViewChild } import { NgModel } from "@angular/forms"; import { BaseComponent } from "../base/base.component"; import { isNumeric } from "jalhyd"; +import { FormulaireDefinition } from "../../formulaire/definition/form-definition"; +import { NgParameter } from "../../formulaire/ngparam"; /** * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur @@ -17,7 +19,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC /** * entité mémoire gérée */ - protected _model: any; // NgBaseParam mais aussi FormDefinition parfois (!?) + protected _model: NgParameter | FormulaireDefinition; // CalcName utilise un FormDefinition ici !? /** * flag de désactivation de l'input @@ -31,9 +33,18 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC public showError = true; /** - * id de l'input, pour accorder <label> et <input> sans ambiguïté + * id de l'input, utilisé notamment pour les tests */ - public inputId = "input1"; + public get inputId() { + let id = "input-1"; + if (this._model) { + // unique input id based on parameter symbol + if (this._model instanceof NgParameter) { + id = (this._model as NgParameter).symbol; + } + } + return id; + } /** * chaîne affichée dans l'input quand aucune valeur n'est saisie @@ -79,8 +90,6 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC constructor(private cdRef: ChangeDetectorRef) { super(); - // generate "unique" input id - this.inputId = "form-" + btoa(String(Math.random())).substring(2, 10); } public get isDisabled(): boolean { diff --git a/src/app/components/generic-select/generic-select.component.html b/src/app/components/generic-select/generic-select.component.html index da7a82515f0406dfc56beb7a36b4e61fc37ac7ac..005ac94275da7445a5a27dbeb24435e2060e82fd 100644 --- a/src/app/components/generic-select/generic-select.component.html +++ b/src/app/components/generic-select/generic-select.component.html @@ -1,5 +1,5 @@ <mat-form-field> - <mat-select [placeholder]="label" [(value)]="selectedValue"> + <mat-select [id]="selectId" [placeholder]="label" [(value)]="selectedValue"> <mat-option *ngFor="let e of entries" [value]="e"> {{ entryLabel(e) }} </mat-option> diff --git a/src/app/components/param-computed/param-computed.component.html b/src/app/components/param-computed/param-computed.component.html index 0751e36bf1dc93de19a451f8aefd886085dc6130..eb582f98d402700628c2885a1ea772e7fa60e078 100644 --- a/src/app/components/param-computed/param-computed.component.html +++ b/src/app/components/param-computed/param-computed.component.html @@ -1,6 +1,6 @@ <!-- a fake input bound to nothing, for the sake of UI consistency --> <mat-form-field> - <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title"> + <input matInput disabled [id]="inputId" class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title"> <button *ngIf="isDicho" mat-icon-button class="param-computed-more" (click)="openDialog()"> <mat-icon>more_horiz</mat-icon> </button> diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts index 6bfced00bab90372af36a4d7946a5aee44dae579..889c3f10bd038dd6fda007d7290ad3c8e2f16981 100644 --- a/src/app/components/param-computed/param-computed.component.ts +++ b/src/app/components/param-computed/param-computed.component.ts @@ -20,6 +20,17 @@ export class ParamComputedComponent { @Input() public title: string; + /** + * id de l'input, utilisé notamment pour les tests + */ + public get inputId() { + let id = "calc_input-1"; + if (this.param) { + id = "calc_" + this.param.symbol; + } + return id; + } + constructor( private editInitialValueDialog: MatDialog, private intlService: I18nService diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html index 5b198816bf96e4b7b3df1aec2f428f7e681d1ec2..b493734349abc13a15f7b14a946fffc78b12eabf 100644 --- a/src/app/components/param-link/param-link.component.html +++ b/src/app/components/param-link/param-link.component.html @@ -1,5 +1,6 @@ <mat-form-field> - <mat-select [name]='"linked-param_" + param.uid' required [placeholder]="param.title" [(ngModel)]="currentLinkedParam"> + <mat-select [id]="selectId" [name]="selectId" [placeholder]="param.title" + [(ngModel)]="currentLinkedParam" required> <mat-option *ngFor="let e of linkableParams" [value]="e"> {{ selectItemLabel(e) }} </mat-option> diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts index 9e28788f706eecf48a47c5836360e400ac127a98..bbc231d26787ae7f477592eab92ca90f68c9365b 100644 --- a/src/app/components/param-link/param-link.component.ts +++ b/src/app/components/param-link/param-link.component.ts @@ -54,6 +54,10 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { private _formService: FormulaireService; + public get selectId() { + return "linked_" + this.param.symbol; + } + constructor( private intlService: I18nService ) { @@ -76,11 +80,9 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { public set currentLinkedParam(p: any) { for (let i = 0; i < this._linkableParams.length; i++) { - if (this._linkableParams[i].value.uid === p.uid) { + if (this._linkableParams[i].nub.uid === p.nub.uid) { this.linkTo(i); break; - } else { - i++; } } } diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts index c96f6c53257d276a45ee07e3a7d8f936d861af85..3ed06955a72693dace890a5bc59c9464524617d4 100644 --- a/src/app/components/results-graph/graph-type.component.ts +++ b/src/app/components/results-graph/graph-type.component.ts @@ -17,6 +17,10 @@ export class GraphTypeSelectComponent implements IObservable { private _observable: Observable; + public get selectId() { + return "graph-type"; // for generic select component + } + constructor(private intlService: I18nService) { this._observable = new Observable(); this._entriesLabels = [ 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 a7840110a5aec8492dcacf86f4fd16775fc2c8b0..61c4cc62a6d7ee0bca2eef3d3236b35bd33a2434 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 @@ -5,7 +5,6 @@ import { SelectEntry } from "../../formulaire/select-entry"; @Component({ selector: "select-field-line", - // templateUrl: "./select-field-line.component.html", templateUrl: "../generic-select/generic-select.component.html", styleUrls: [ "./select-field-line.component.scss" @@ -15,6 +14,10 @@ export class SelectFieldLineComponent { @Input() private _select: SelectField; + public get selectId() { + return this._select.id; + } + public get entries(): SelectEntry[] { if (! this._select) { return []; diff --git a/src/app/formulaire/definition/form-def-section.ts b/src/app/formulaire/definition/form-def-section.ts index f4fe312ca445de92751bd3f3db6fa3ea73b42120..9ad2dbf71baf45b31384f8558417fe3a583b559c 100644 --- a/src/app/formulaire/definition/form-def-section.ts +++ b/src/app/formulaire/definition/form-def-section.ts @@ -1,10 +1,3 @@ -import { - ComputeNodeType, ParamsSectionTrapez, cSnTrapez, ParamsSectionRectang, cSnRectang, - ParamsSectionCirc, cSnCirc, ParamsSectionPuiss, cSnPuiss, acSection, ParamsEquation -} from "jalhyd"; - -import { SelectField } from "../select-field"; -import { Field } from "../field"; import { NgParameter, ParamRadioConfig } from "../ngparam"; import { FormulaireDefinition } from "./form-definition"; import { FieldSet } from "../fieldset"; diff --git a/src/app/formulaire/formulaire-node.ts b/src/app/formulaire/formulaire-node.ts index 627e38f7d9225419a966d63c0d0d96801dede6c0..128f39a675a5501b93bc4bb4692c411e4c910f1d 100644 --- a/src/app/formulaire/formulaire-node.ts +++ b/src/app/formulaire/formulaire-node.ts @@ -56,6 +56,11 @@ export abstract class FormulaireNode implements IObservable { return this._uid; } + /** utiliser uniquement pour charger des sessions (désérialisation JSON) */ + public setUid(uid: string) { + this._uid = uid; + } + /** * cherche un FormulaireNode par son id de conf */ diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts index 82cca313a78d8406fadd269a9385505202866da5..1c82a275adb49d278b97f3ed65f4fe198f67fe51 100644 --- a/src/app/formulaire/ngparam.ts +++ b/src/app/formulaire/ngparam.ts @@ -304,7 +304,6 @@ export class NgParameter extends InputField implements Observer { if (super.verifiesDependency(d)) { return true; } - switch (d.masterCondition.type) { case DependencyConditionType.HasValue: { const mc: ValueDependencyCondition = <ValueDependencyCondition>d.masterCondition; @@ -326,21 +325,20 @@ export class NgParameter extends InputField implements Observer { res["mode"] = ParamValueMode[vm]; switch (vm) { case ParamValueMode.SINGLE: - res["value"] = this._paramValues.singleValue; + res["value"] = this.getValue(); break; case ParamValueMode.MINMAX: - res["min"] = this._paramValues.min; - res["max"] = this._paramValues.max; - res["step"] = this._paramValues.step; + res["min"] = this.minValue; + res["max"] = this.maxValue; + res["step"] = this.stepValue; break; case ParamValueMode.LISTE: - res["values"] = this._paramValues.valueList; + res["values"] = this.valueList; break; case ParamValueMode.LINK: - // @TODO copié à l'arrache, vérifier que ça marche res["form_uid"] = ServiceFactory.instance.formulaireService.getFormulaireFromNubId(this._paramDef.referencedNub["uid"]).uid; res["ref"] = this.paramDefinition.referenceDefinition; break; @@ -378,7 +376,19 @@ export class NgParameter extends InputField implements Observer { break; case ParamValueMode.LINK: - break; // cf FormulaireService.updateParamsLinks() + const uid: string = json["form_uid"]; + const ref: string = json["ref"]; + this._paramValues.valueMode = ParamValueMode.LINK; + // formulaire dont le Nub est la cible du lien + const destForm = ServiceFactory.instance.formulaireService.getFormulaireFromId(uid); + if (destForm) { + this._paramValues.defineReference(destForm.currentNub, ref); + } else { + // @TODO et si la cible du lien n'existe pas ?? + // cf FormulaireService.updateParamsLinks() + console.log("LA CIBLE DU LIEN N'EXISTE PAS !!"); + } + break; default: throw new Error(`session file : invalid value mode '${json["mode"]}' in param object`); diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts index 3bfadc6cf957cd3df97395df4cb7344aafd4ee75..a8162bac46fbaf3563160d4ae82548ed13faacac 100644 --- a/src/app/formulaire/select-field.ts +++ b/src/app/formulaire/select-field.ts @@ -43,6 +43,7 @@ export class SelectField extends Field { public setValue(v: SelectEntry) { if (this._selectedEntry !== v) { + console.log("++++ select field : notify observers", v); this._selectedEntry = v; this.notifyObservers({ "action": "select", @@ -145,7 +146,7 @@ export class SelectField extends Field { const sel = elements[k]; for (const e of this._entries) { if (e.id === sel) { - this._selectedEntry = e; + this.setValue(e); break; } } diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts index dda5ad7a3f3d766173caec498ca3309bfdd1fcc6..8be740fa20060504a1e71e02822e473b9664318d 100644 --- a/src/app/services/formulaire/formulaire.service.ts +++ b/src/app/services/formulaire/formulaire.service.ts @@ -81,6 +81,20 @@ export class FormulaireService extends Observable { }); } + /** + * retourne true si l'uid passé en paramètre est déjà utilisé par un formulaire + * @param uid uid à tester + */ + private formUIDAlreadyUsed(uid: string): boolean { + let alreadyUsed = false; + for (const f of this._formulaires) { + if (f.uid === uid) { + alreadyUsed = true; + } + } + return alreadyUsed; + } + public updateLocalisation() { for (const c of EnumEx.getValues(CalculatorType)) { const prom: Promise<StringMap> = this.loadLocalisation(c); @@ -149,10 +163,17 @@ export class FormulaireService extends Observable { /** * crée un formulaire d'un type donné * @param ct type de formulaire - * @param jsonState + * @param jsonState descripteur sérialisé du formulaire, le cal échéant */ public createFormulaire(ct: CalculatorType, jsonState?: {}): Promise<FormulaireDefinition> { const f: FormulaireDefinition = this.newFormulaire(ct); + // conserver l'UID d'origine, sauf en cas de collision + if (jsonState !== undefined) { + const originalUID = jsonState["uid"]; + if (originalUID !== undefined && ! this.formUIDAlreadyUsed(originalUID)) { + f.setUid(originalUID); + } + } this._formulaires.push(f); const prom: Promise<any> = this.loadConfig(ct); @@ -424,7 +445,7 @@ export class FormulaireService extends Observable { }); } - private deserialiseForm(elements: {}): Promise<FormulaireDefinition> { + public deserialiseForm(elements: {}): Promise<FormulaireDefinition> { const props = elements["props"]; const ct: CalculatorType = props["calcType"]; return this.createFormulaire(ct, elements); @@ -441,6 +462,7 @@ export class FormulaireService extends Observable { const form = element[keys[0]]; for (const i of formInfos) { + // on the loadSession dialog, was the checkbox checked for this calculator ? if (i["uid"] === form["uid"] && i["selected"]) { return this.deserialiseForm(form); } diff --git a/src/styles.scss b/src/styles.scss index 013d064f64343a703326db51277fd2409092bfa2..ae70c285c30da9d28cefe7c73a789f2c1069048a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -6,7 +6,11 @@ html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } +body { + margin: 0; + font-family: Roboto, "Helvetica Neue", sans-serif; + background-color: #FAFAFA; // harmonize with app-content +} button { &:focus { @@ -40,6 +44,10 @@ mat-form-field { } } +.file-input-field { + cursor: pointer; +} + .eight-em-bottom-padding { padding-bottom: 8em; }