diff --git a/e2e/calc-all-examples.e2e-spec.ts b/e2e/calc-all-examples.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..31a63bbabd460eb3642394a59963e16ac00ee999
--- /dev/null
+++ b/e2e/calc-all-examples.e2e-spec.ts
@@ -0,0 +1,57 @@
+import { ListPage } from "./list.po";
+import { CalculatorPage } from "./calculator.po";
+import { Navbar } from "./navbar.po";
+import { browser, by, element } from "protractor";
+
+/**
+ * Calculate all modules of all examples
+ */
+describe("ngHyd − calculate all modules of all examples −", async () => {
+
+  let listPage: ListPage;
+  let calcPage: CalculatorPage;
+  let navbar: Navbar;
+
+  beforeAll(() => {
+    jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
+  });
+
+  it("calcul de tous les modules de tous les exemples −", async () => {
+    listPage = new ListPage();
+    calcPage = new CalculatorPage();
+    navbar = new Navbar();
+    listPage = new ListPage();
+
+    // for each calculator
+    let lastExampleFound = true;
+    let i = 0;
+    while (lastExampleFound) {
+      await listPage.navigateTo();
+
+      const examples = await element.all(by.css("#examples-list .load-example"));
+      if (examples.length > i) {
+        // click example #i
+        await examples[i].click();
+        await browser.sleep(50);
+
+        const nbModules = await navbar.getCalculatorEntriesCount();
+        for (let j = 0; j < nbModules; j++) {
+          // select module
+          await navbar.openNthCalculator(j);
+          await browser.sleep(50);
+          // calculate module
+          await calcPage.getCalculateButton().click();
+          // check results
+          const hasResults = await calcPage.hasValidResults();
+          expect(hasResults).toBe(true, `example ${i + 1}, module ${j + 1} (starting at 1)`);
+        }
+      } else {
+        // no more examples
+        lastExampleFound = false;
+      }
+      i++;
+    }
+
+  });
+
+});
diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index 0e6f9f322c5c18d0698058e68032838b87b42793..3dda3f42d72b7d0ecfb0a98879602d15212072c2 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -71,6 +71,14 @@ export class CalculatorPage {
     return element.all(by.css("mat-button-toggle.radio_link"));
   }
 
+  getPabResultsTable() {
+    return element(by.css(".pab-results-table-inner-container table"));
+  }
+
+  getVariatedResultsTable() {
+    return element(by.css(".var-results-inner-container table"));
+  }
+
   getAllVariatedResultsTableHeaders() {
     return element.all(by.css("var-results table thead th"));
   }
@@ -89,6 +97,10 @@ export class CalculatorPage {
     return element(by.css(".fixed-results-inner-container table"));
   }
 
+  getAllFixedResultsRows() {
+    return element.all(by.css("fixed-results table tbody tr"));
+  }
+
   /** return nth <tr> of given <table>, starting at 1 */
   getNthRow(table: ElementFinder, n: number) {
     return table.element(by.css("tbody > tr:nth-of-type(" + n + ")"));
@@ -123,6 +135,65 @@ export class CalculatorPage {
     );
   }
 
+  /**
+   * For a given <table> element, check that values of all cells of all rows in <tbody> are
+   * different from "NaN", "ERR" and optionally ""
+   * @param table a <table> ElementFinder
+   * @param allowEmpty if true, empty "" values will be considered valid
+   */
+  async allRowsHaveValidResults(table: ElementFinder, allowEmpty: boolean = false): Promise<boolean> {
+    let n = 0;
+    let ok = true;
+    const invalidValues = [ "ERR", "NaN" ];
+    if (! allowEmpty) {
+      invalidValues.push("");
+    }
+
+    const nbCols = await table.all(by.css("thead th")).count();
+    const rows = await table.all(by.css("tbody tr"));
+
+    for (const row of rows) {
+      for (let i = 0; i < nbCols; i++) {
+        // get i_th column of n_th row
+        const val = await row.element(by.css("td:nth-of-type(" + (i + 1) + ")")).getText();
+        // console.log(`TABLE VAL ${n}/${i}=>`, val);
+        ok = ok && ! invalidValues.includes(val);
+      }
+      n++;
+    }
+    return ok;
+  }
+
+  /**
+   * Returns true if this.hasResults() is true, and if results table are
+   * not empty and contain only valid values (no "NaN", no "", no "ERR")
+   */
+  async hasValidResults(): Promise<boolean> {
+    let ok = false;
+    if (await this.hasResults()) {
+      ok = true;
+      // check fixed results
+      const frt = this.getFixedResultsTable();
+      if (await frt.isPresent() && await frt.isDisplayed()) {
+        console.log("==============> Fixed found !");
+        ok = ok && await this.allRowsHaveValidResults(frt);
+      }
+      // check variated results
+      const vrt = this.getVariatedResultsTable();
+      if (await vrt.isPresent() && await vrt.isDisplayed()) {
+        console.log("==============> Var found !");
+        ok = ok && await this.allRowsHaveValidResults(vrt);
+      }
+      // check PAB results
+      const prt = this.getPabResultsTable();
+      if (await prt.isPresent() && await prt.isDisplayed()) {
+        console.log("==============> Pab found !");
+        ok = ok && await this.allRowsHaveValidResults(prt, true);
+      }
+    }
+    return ok;
+  }
+
   async hasLog() {
     return await this.nbLogEntries() > 0;
   }
diff --git a/e2e/navbar.po.ts b/e2e/navbar.po.ts
index 13b1efc1f8c5c828ed9a99b903536f817804b69c..05273efdee188484ce85ba4ebb3054287095000f 100644
--- a/e2e/navbar.po.ts
+++ b/e2e/navbar.po.ts
@@ -1,10 +1,46 @@
-import { by, element } from "protractor";
+import { by, element, protractor } from "protractor";
 
 export class Navbar {
   getAllCalculatorTabs() {
     return element.all(by.css("#tabs-container button.calculator-button"));
   }
 
+  /**
+   * Robust method that returns number of calculator entries,
+   * whether they are buttons or dropdown select options
+   */
+  async getCalculatorEntriesCount() {
+    // if dropDown calculators select is visible
+    const dropDown = element(by.css("mat-select#selectCalculator"));
+    if (await dropDown.isPresent() && await dropDown.isDisplayed()) {
+      await dropDown.click();
+      const options = (await dropDown.getAttribute("aria-owns")).split(" ");
+      await dropDown.sendKeys(protractor.Key.ESCAPE); // close dropdown
+      return options.length;
+    } else {
+      return (await element.all(by.css("#tabs-container button.calculator-button"))).length;
+    }
+  }
+
+  /**
+   * Robust method that opens the nth calculator, whether the
+   * calculator selector is a list of buttons or a dropdown select
+   */
+  async openNthCalculator(n: number) {
+    // if dropDown calculators select is visible
+    const dropDown = element(by.css("mat-select#selectCalculator"));
+    if (await dropDown.isPresent() && await dropDown.isDisplayed()) {
+      await dropDown.click();
+      const options = (await dropDown.getAttribute("aria-owns")).split(" ");
+      const optId = options[n];
+      const option = element(by.id(optId));
+      await option.click();
+    } else {
+      const tabs = this.getAllCalculatorTabs();
+      await tabs.get(n).click();
+    }
+  }
+
   getCalculatorTabForUid(uid: string) {
     return element(by.css("#tabs-container button.calculator-button.calculator-uid-" + uid));
   }