<commit_before>/**
 * @module modules/ct-scaffold-sk
 * @description
ct-scaffold-sk

 *
 * Contains the title bar, side bar, and error-toast for all the CT pages. The rest of
 * every CT page should be a child of this element.
 *
 * Has a spinner-sk that can be activated when it hears "begin-task" events and keeps
 * spinning until it hears an equal number of "end-task" events.
 *
 * The error-toast element responds to fetch-error events and normal error-sk events.
 *
 * @attr {string} app_title - The title to show in the page banner.
 *
 * @attr {boolean} testing_offline - If we should operate entirely in offline mode.
 */
import { define } from 'elements-sk/define';
import { fromObject } from 'common-sk/modules/query';
import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
import { errorMessage } from 'elements-sk/errorMessage';
import { html } from 'lit-html';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import * as ctfe_utils from '../ctfe_utils';

import '../../../infra-sk/modules/app-sk';
import '../../../infra-sk/modules/login-sk';
import '../../../infra-sk/modules/theme-chooser-sk';

import 'elements-sk/error-toast-sk';
import 'elements-sk/icon/assessment-icon-sk';
import 'elements-sk/icon/find-in-page-icon-sk';
import 'elements-sk/icon/folder-icon-sk';
import 'elements-sk/icon/help-icon-sk';
import 'elements-sk/icon/home-icon-sk';
import 'elements-sk/icon/label-icon-sk';
import 'elements-sk/icon/photo-camera-icon-sk';
import 'elements-sk/icon/list-icon-sk';
import 'elements-sk/icon/search-icon-sk';
import 'elements-sk/icon/sync-problem-icon-sk';
import 'elements-sk/icon/view-day-icon-sk';
import 'elements-sk/spinner-sk';

import 'elements-sk/icon/trending-up-icon-sk';

import 'elements-sk/icon/assessment-icon-sk';
import 'elements-sk/icon/cloud-icon-sk';

import 'elements-sk/icon/build-icon-sk';
import 'elements-sk/icon/person-icon-sk';
import 'elements-sk/icon/reorder-icon-sk';
import 'elements-sk/icon/history-icon-sk';

const template = (ele) => html`
<app-sk>

${ele.appTitle}

      <spinner-sk></spinner-sk>

    <login-sk ?testing_offline=${ele.testingOffline}></login-sk>
    <theme-chooser-sk></theme-chooser-sk>

        <trending-up-icon-sk></trending-up-icon-sk>Performance

        <search-icon-sk></search-icon-sk>Analysis

        <assessment-icon-sk></assessment-icon-sk>Metrics Analysis

        <photo-camera-icon-sk></photo-camera-icon-sk>SKPictures

        <build-icon-sk></build-icon-sk>Builds

        <person-icon-sk></person-icon-sk>Admin Tasks

        <reorder-icon-sk></reorder-icon-sk>View Queue (${ele._task_queue_length})

        <history-icon-sk></history-icon-sk>Runs History

        <folder-icon-sk></folder-icon-sk>Code

        <help-icon-sk></help-icon-sk>Docs

  <error-toast-sk></error-toast-sk>
</app-sk>
`;

/**
 * Moves the elements from one NodeList to another NodeList.
 *
 * @param {NodeList} from - The list we are moving from.
 * @param {NodeList} to - The list we are moving to.
 */
function move(from, to) {
  Array.prototype.slice.call(from).forEach((ele) => to.appendChild(ele));
}

define('ct-scaffold-sk', class extends ElementSk {
  constructor() {
    super(template);
    this._main = null;
    this._busyTaskCount = 0;
    this._task_queue_length = 0;
    this._spinner = null;
  }

  connectedCallback() {
    super.connectedCallback();
    // Don't call more than once.
    if (this._main) {
      return;
    }
    this.addEventListener('begin-task', this._addBusyTask);
    this.addEventListener('end-task', this._finishedTask);
    this.addEventListener('fetch-error', this._fetchError);

    ctfe_utils.taskDescriptors.forEach((obj) => {
      const queryParams = {
        size: 1,
        not_completed: true,
      };
      const queryStr = `?${fromObject(queryParams)}`;
      fetch(obj.get_url + queryStr, { method: 'POST' })
        .then(jsonOrThrow)
        .then((json) => {
          this._task_queue_length += json.pagination.total;
        })
        .catch(errorMessage);
    });
    // We aren't using shadow dom so we need to manually move the children of
    // ct-scaffold-sk to be children of 'main'. We have to do this for the
    // existing elements and for all future mutations.

    // Create a temporary holding spot for elements we're moving.
    const div = document.createElement('div');
    move(this.children, div);

    // Now that we've moved all the old children out of the way we can render
    // the template.
    this._render();

    this._spinner = this.querySelector('header spinner-sk');

    // Move the old children back under main.
    this._main = this.querySelector('main');
    move(div.children, this._main);

    // Move all future children under main also.
    const observer = new MutationObserver((mutList) => {
      mutList.forEach((mut) => {
        move(mut.addedNodes, this._main);
      });
    });
    observer.observe(this, { childList: true });
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.removeEventListener('begin-task', this._addBusyTask);
    this.removeEventListener('end-task', this._finishedTask);
    this.removeEventListener('fetch-error', this._fetchError);
  }

  /** @prop appTitle {string} Reflects the app_title attribute for ease of use. */
  get appTitle() { return this.getAttribute('app_title'); }

  set appTitle(val) { this.setAttribute('app_title', val); }

  /** @prop {boolean} busy Indicates if there any on-going tasks (e.g. RPCs).
   *                  This also mirrors the status of the embedded spinner-sk.
   *                  Read-only. */
  get busy() { return !!this._busyTaskCount; }

  /** @prop testingOffline {boolean} Reflects the testing_offline attribute for ease of use.
   */
  get testingOffline() { return this.hasAttribute('testing_offline'); }

  set testingOffline(val) {
    if (val) {
      this.setAttribute('testing_offline', '');
    } else {
      this.removeAttribute('testing_offline');
    }
  }

  /**
   * Indicate there are some number of tasks (e.g. RPCs) the app is waiting on
   * and should be in the "busy" state, if it isn't already.
   *
   */
  _addBusyTask() {
    this._busyTaskCount++;
    if (this._spinner && this._busyTaskCount > 0) {
      this._spinner.active = true;
    }
  }

  /**
   * Removes one task from the busy count. If there are no more tasks to wait
   * for, the app will leave the "busy" state and emit the "busy-end" event.
   *
   */
  _finishedTask() {
    this._busyTaskCount--;
    if (this._busyTaskCount <= 0) {
      this._busyTaskCount = 0;
      if (this._spinner) {
        this._spinner.active = false;
      }
      this.dispatchEvent(new CustomEvent('busy-end', { bubbles: true }));
    }
  }

  /** Handles a fetch error. Expects the detail of error to contain:
   *  error: the error given to fetch.
   *  loading: A string explaining what was being fetched.
   */
  _fetchError(e) {
    const loadingWhat = e.detail.loading;
    e = e.detail.error;
    if (e.name !== 'AbortError') {
      // We can ignore AbortError since they fire anytime an AbortController was canceled.
      // Chrome and Firefox report a DOMException in this case:
      // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
      errorMessage(`Unexpected error loading ${loadingWhat}: ${e.message}`,
        5000);
    }
    this._finishedTask();
  }
});
<commit_msg>[ct] Re-render ct-scaffold once we have queue length.<commit_after>/**
 * @module modules/ct-scaffold-sk
 * @description
ct-scaffold-sk

 *
 * Contains the title bar, side bar, and error-toast for all the CT pages. The rest of
 * every CT page should be a child of this element.
 *
 * Has a spinner-sk that can be activated when it hears "begin-task" events and keeps
 * spinning until it hears an equal number of "end-task" events.
 *
 * The error-toast element responds to fetch-error events and normal error-sk events.
 *
 * @attr {string} app_title - The title to show in the page banner.
 *
 * @attr {boolean} testing_offline - If we should operate entirely in offline mode.
 */
import { define } from 'elements-sk/define';
import { fromObject } from 'common-sk/modules/query';
import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
import { errorMessage } from 'elements-sk/errorMessage';
import { html } from 'lit-html';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import * as ctfe_utils from '../ctfe_utils';

import '../../../infra-sk/modules/app-sk';
import '../../../infra-sk/modules/login-sk';
import '../../../infra-sk/modules/theme-chooser-sk';

import 'elements-sk/error-toast-sk';
import 'elements-sk/icon/assessment-icon-sk';
import 'elements-sk/icon/find-in-page-icon-sk';
import 'elements-sk/icon/folder-icon-sk';
import 'elements-sk/icon/help-icon-sk';
import 'elements-sk/icon/home-icon-sk';
import 'elements-sk/icon/label-icon-sk';
import 'elements-sk/icon/photo-camera-icon-sk';
import 'elements-sk/icon/list-icon-sk';
import 'elements-sk/icon/search-icon-sk';
import 'elements-sk/icon/sync-problem-icon-sk';
import 'elements-sk/icon/view-day-icon-sk';
import 'elements-sk/spinner-sk';

import 'elements-sk/icon/trending-up-icon-sk';

import 'elements-sk/icon/assessment-icon-sk';
import 'elements-sk/icon/cloud-icon-sk';

import 'elements-sk/icon/build-icon-sk';
import 'elements-sk/icon/person-icon-sk';
import 'elements-sk/icon/reorder-icon-sk';
import 'elements-sk/icon/history-icon-sk';

const template = (ele) => html`
<app-sk>

${ele.appTitle}

      <spinner-sk></spinner-sk>

    <login-sk ?testing_offline=${ele.testingOffline}></login-sk>
    <theme-chooser-sk></theme-chooser-sk>

        <trending-up-icon-sk></trending-up-icon-sk>Performance

        <search-icon-sk></search-icon-sk>Analysis

        <assessment-icon-sk></assessment-icon-sk>Metrics Analysis

        <photo-camera-icon-sk></photo-camera-icon-sk>SKPictures

        <build-icon-sk></build-icon-sk>Builds

        <person-icon-sk></person-icon-sk>Admin Tasks

        <reorder-icon-sk></reorder-icon-sk>View Queue (${ele._task_queue_length})

        <history-icon-sk></history-icon-sk>Runs History

        <folder-icon-sk></folder-icon-sk>Code

        <help-icon-sk></help-icon-sk>Docs

  <error-toast-sk></error-toast-sk>
</app-sk>
`;

/**
 * Moves the elements from one NodeList to another NodeList.
 *
 * @param {NodeList} from - The list we are moving from.
 * @param {NodeList} to - The list we are moving to.
 */
function move(from, to) {
  Array.prototype.slice.call(from).forEach((ele) => to.appendChild(ele));
}

define('ct-scaffold-sk', class extends ElementSk {
  constructor() {
    super(template);
    this._main = null;
    this._busyTaskCount = 0;
    this._task_queue_length = 0;
    this._spinner = null;
  }

  connectedCallback() {
    super.connectedCallback();
    // Don't call more than once.
    if (this._main) {
      return;
    }
    this.addEventListener('begin-task', this._addBusyTask);
    this.addEventListener('end-task', this._finishedTask);
    this.addEventListener('fetch-error', this._fetchError);

    const allFetches = [];
    ctfe_utils.taskDescriptors.forEach((obj) => {
      const queryParams = {
        size: 1,
        not_completed: true,
      };
      const queryStr = `?${fromObject(queryParams)}`;
      allFetches.push(fetch(obj.get_url + queryStr, { method: 'POST' })
        .then(jsonOrThrow)
        .then((json) => {
          this._task_queue_length += json.pagination.total;
        })
        .catch(errorMessage));
    });
    // We aren't using shadow dom so we need to manually move the children of
    // ct-scaffold-sk to be children of 'main'. We have to do this for the
    // existing elements and for all future mutations.

    // Create a temporary holding spot for elements we're moving.
    const div = document.createElement('div');
    move(this.children, div);

    // Now that we've moved all the old children out of the way we can render
    // the template.
    this._render();

    this._spinner = this.querySelector('header spinner-sk');

    // Move the old children back under main.
    this._main = this.querySelector('main');
    move(div.children, this._main);

    // Move all future children under main also.
    const observer = new MutationObserver((mutList) => {
      mutList.forEach((mut) => {
        move(mut.addedNodes, this._main);
      });
    });
    observer.observe(this, { childList: true });
    // Once we've loaded the queue length, re-render.
    Promise.all(allFetches).then(() => this._render());
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.removeEventListener('begin-task', this._addBusyTask);
    this.removeEventListener('end-task', this._finishedTask);
    this.removeEventListener('fetch-error', this._fetchError);
  }

  /** @prop appTitle {string} Reflects the app_title attribute for ease of use. */
  get appTitle() { return this.getAttribute('app_title'); }

  set appTitle(val) { this.setAttribute('app_title', val); }

  /** @prop {boolean} busy Indicates if there any on-going tasks (e.g. RPCs).
   *                  This also mirrors the status of the embedded spinner-sk.
   *                  Read-only. */
  get busy() { return !!this._busyTaskCount; }

  /** @prop testingOffline {boolean} Reflects the testing_offline attribute for ease of use.
   */
  get testingOffline() { return this.hasAttribute('testing_offline'); }

  set testingOffline(val) {
    if (val) {
      this.setAttribute('testing_offline', '');
    } else {
      this.removeAttribute('testing_offline');
    }
  }

  /**
   * Indicate there are some number of tasks (e.g. RPCs) the app is waiting on
   * and should be in the "busy" state, if it isn't already.
   *
   */
  _addBusyTask() {
    this._busyTaskCount++;
    if (this._spinner && this._busyTaskCount > 0) {
      this._spinner.active = true;
    }
  }

  /**
   * Removes one task from the busy count. If there are no more tasks to wait
   * for, the app will leave the "busy" state and emit the "busy-end" event.
   *
   */
  _finishedTask() {
    this._busyTaskCount--;
    if (this._busyTaskCount <= 0) {
      this._busyTaskCount = 0;
      if (this._spinner) {
        this._spinner.active = false;
      }
      this.dispatchEvent(new CustomEvent('busy-end', { bubbles: true }));
    }
  }

  /** Handles a fetch error. Expects the detail of error to contain:
   *  error: the error given to fetch.
   *  loading: A string explaining what was being fetched.
   */
  _fetchError(e) {
    const loadingWhat = e.detail.loading;
    e = e.detail.error;
    if (e.name !== 'AbortError') {
      // We can ignore AbortError since they fire anytime an AbortController was canceled.
      // Chrome and Firefox report a DOMException in this case:
      // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
      errorMessage(`Unexpected error loading ${loadingWhat}: ${e.message}`,
        5000);
    }
    this._finishedTask();
  }
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015-2016 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {login, getResult, checkResults, checkResultsOrNot} from "../plugins/utils.js";
import {TIMEOUT} from "../plugins/constants.js";

context("14 - RGA Browser", () => {
    before(() => {
        login();
    });
    /* "geneName",
        "IndividualId",
        "numParents",
        "cohort",
        "populationFrequencyAlt",
        "type",
        "consequenceType",
        "clinicalSignificance"*/
    it("14.1 - Gene View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='gene-tab']", {timeout: TIMEOUT}).click({force: true});

        checkResults("rga-gene-grid");

        // gene Name
        // queries for the first gene and then check if the first result contains the gene.
        let geneName;
        getResult("rga-gene-grid", 0).then($text => {
            geneName = $text;
            // console.log("geneName", geneName);
            cy.get("feature-filter input[type='text']").type(geneName + "{enter}");
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-gene-grid");
            getResult("rga-gene-grid", 0).then($resultCell => {
                cy.wrap($resultCell).should("contain", geneName);

            });
        });

        // knockoutType
        cy.get("section-filter#Gene div[data-cy='knockoutType-content'] button").click();
        cy.get("section-filter#Gene div[data-cy='knockoutType-content'] .dropdown-menu a").contains("COMP_HET").click();
        cy.get("div.search-button-wrapper button").click();
        checkResults("rga-gene-grid");

        // set numParents=2
        cy.get("section-filter#Confidence .magic-checkbox-wrapper > :nth-child(3) > label").click();
        cy.get("div.search-button-wrapper button").click();
        checkResults("rga-gene-grid");

        // checking the number of CH Definite is > 0 (the current query is geneName=XXX,knockoutType=COMP_HET,numParents=2)
        getResult("rga-gene-grid", 3).then($CHDefiniteNum => {
            //expect($div.text().trim()).gt(0)
            assert.isAbove(Number($CHDefiniteNum), 0, "Results")
        });

        //cy.get("opencga-active-filters button[data-filter-name='knockoutType']").click();

        cy.get("button.active-filter-label").click()
        cy.get("a[data-action='active-filter-clear']").click()
        checkResults("rga-gene-grid");

    });

    it("14.2 - Individual View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='individual-tab']", {timeout: TIMEOUT}).click({force: true});

        checkResults("rga-individual-grid");

        // queries for the first gene and then check if the first result contains the gene.
        let IndividualId;
        getResult("rga-individual-grid", 0).then($text => {
            IndividualId = $text;
            console.log("IndividualId i", IndividualId);
            cy.get("div[data-cy='individualId-content'] input[type='text']").type(IndividualId + "{enter}");
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-individual-grid");

            getResult("rga-individual-grid", 0).then($resultCell => {
                console.log("$TEXT", $resultCell);
                cy.wrap($resultCell).should("contain", IndividualId);

            });
        });
        cy.get("button.active-filter-label").click()
        cy.get("a[data-action='active-filter-clear']").click()
        checkResults("rga-gene-grid");

    });

    it("14.3 - Variant View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='variant-tab']", {timeout: TIMEOUT}).click({force: true});

        checkResults("rga-variant-view");

        cy.get("button.active-filter-label").click();
        cy.get("a[data-action='active-filter-clear']").click();

        checkResults("rga-variant-view");

        // variant Id
        getResult("rga-variant-view", 0).then(variantId => {
            const region = variantId.trim().match(/\d+:\d+/)[0];
            cy.get("region-filter textarea").type(region);
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-variant-view");
            getResult("rga-variant-view", 0).then($resultCell => {
                cy.wrap($resultCell).should("contain", region);

            });
        });

        cy.get("opencga-active-filters button[data-filter-name='region']").click();
        checkResults("rga-variant-view");

        // gene Name
        getResult("rga-variant-view", 1).then(geneName => {
            console.log("geneName", geneName);
            cy.get("feature-filter input[type='text']").type(geneName + "{enter}");
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-variant-view");
            getResult("rga-variant-view", 1).then($resultCell => {
                cy.wrap($resultCell).should("contain", geneName);

            });
        });

    });
});
<commit_msg>E2E Rga Browser improved. Closes #229<commit_after>/**
 * Copyright 2015-2016 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {login, getResult, checkResults, checkResultsOrNot} from "../plugins/utils.js";
import {TIMEOUT} from "../plugins/constants.js";

context("14 - RGA Browser", () => {
    before(() => {
        login();
    });
    /* "geneName",
        "IndividualId",
        "numParents",
        "cohort",
        "populationFrequencyAlt",
        "type",
        "consequenceType",
        "clinicalSignificance"*/
    it("14.1 - Gene View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='gene-tab']", {timeout: TIMEOUT}).click({force: true});

        cy.get("rga-gene-view .columns-toggle-wrapper button").should("be.visible").and("contain", "Columns").click();
        cy.get("rga-gene-view .columns-toggle-wrapper ul li").and("have.length.gt", 1);

        cy.get("rga-gene-view .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // deactivate all the columns
        cy.get("rga-gene-view .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.lengthOf", 0);

        cy.get("rga-gene-view .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // reactivate all the columns
        cy.get("rga-gene-view .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.length.gt", 1);

        checkResults("rga-gene-view");

        // gene Name
        // queries for the first gene and then check if the first result contains the gene.
        let geneName;
        getResult("rga-gene-view", 0).then($text => {
            geneName = $text;
            cy.get("feature-filter input[type='text']").type(geneName);
            cy.get("feature-filter ul.dropdown-menu li").should("have.lengthOf", 1);

            // occasionally fails
            // Timed out retrying after 4050ms: cy.click() failed because this element is detached from the DOM.
            // DMKN
            cy.wait(500);
            cy.get("feature-filter ul.dropdown-menu li.active a").click({force: true});
            cy.get("div.search-button-wrapper button").click();

            checkResults("rga-gene-view");
            getResult("rga-gene-view", 0).then($resultCell => {
                cy.wrap($resultCell).should("contain", geneName);

            });
        });

        // knockoutType
        cy.get("section-filter#Gene div[data-cy='knockoutType'] button").click();
        cy.get("section-filter#Gene div[data-cy='knockoutType'] .dropdown-menu a").contains("COMP_HET").click();
        cy.get("div.search-button-wrapper button").click();
        checkResults("rga-gene-view");

        // set numParents=2
        cy.get("section-filter#Confidence .magic-checkbox-wrapper > :nth-child(3) > label").click();
        cy.get("div.search-button-wrapper button").click();
        checkResults("rga-gene-view");

        // checking the number of CH Definite is > 0 (the current query is geneName=XXX,knockoutType=COMP_HET,numParents=2)
        getResult("rga-gene-view", 3).then($CHDefiniteNum => {
            // expect($div.text().trim()).gt(0)
            assert.isAbove(Number($CHDefiniteNum), 0, "Results");
        });

        // cy.get("opencga-active-filters button[data-filter-name='knockoutType']").click();

        cy.get("button.active-filter-label").click();
        cy.get("a[data-action='active-filter-clear']").click();
        checkResults("rga-gene-view");

    });
    it("14.2 - Individual View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='individual-tab']", {timeout: TIMEOUT}).click({force: true});

        checkResults("rga-individual-view");

        cy.get("rga-individual-view .columns-toggle-wrapper button").should("be.visible").and("contain", "Columns").click();
        cy.get("rga-individual-view .columns-toggle-wrapper ul li").and("have.length.gt", 1);

        cy.get("rga-individual-view .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // deactivate all the columns
        cy.get("rga-individual-view .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.lengthOf", 1);

        cy.get("rga-individual-view .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // reactivate all the columns
        cy.get("rga-individual-view .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.length.gt", 1);

        // queries for the first gene and then check if the first result contains the gene.
        let IndividualId;
        getResult("rga-individual-view", 0).then($text => {
            IndividualId = $text;
            console.log("IndividualId i", IndividualId);
            cy.get("div[data-cy='individualId'] input[type='text']").type(IndividualId + "{enter}");
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-individual-view");

            getResult("rga-individual-view", 0).then($resultCell => {
                console.log("$resultCell", $resultCell);
                cy.wrap($resultCell).should("contain", IndividualId);

            });

            // check detail-tabs family table
            // ensure `IndividualId` is present in table header
            cy.get("rga-individual-family table > thead > :nth-child(1) > :nth-child(7) > .th-inner").contains(IndividualId);

        });
        cy.get("button.active-filter-label").click();
        cy.get("a[data-action='active-filter-clear']").click();
        checkResults("rga-individual-view");

    });
    it("14.3 - Variant View", () => {
        cy.get("a[data-id=rga]", {timeout: TIMEOUT}).click({force: true});
        cy.get("div.page-title h2", {timeout: TIMEOUT}).should("be.visible").and("contain", "Recessive Variant Browser");
        cy.get("button[data-tab-id='variant-tab']", {timeout: TIMEOUT}).click({force: true});

        checkResults("rga-variant-view");

        cy.get("rga-variant-view > .container-fluid .columns-toggle-wrapper button").should("be.visible").and("contain", "Columns").click();
        cy.get("rga-variant-view > .container-fluid .columns-toggle-wrapper ul li").and("have.length.gt", 1);

        cy.get("rga-variant-view > .container-fluid .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // deactivate all the columns
        cy.get("rga-variant-view > .container-fluid > .row .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.lengthOf", 0);

        cy.get("rga-variant-view > .container-fluid .columns-toggle-wrapper ul li a").click({multiple: true, timeout: TIMEOUT}); // reactivate all the columns
        cy.get("rga-variant-view > .container-fluid > .row .bootstrap-table .fixed-table-container tr[data-index=0] > td", {timeout: TIMEOUT}).should("have.length.gt", 1);

        cy.get("button.active-filter-label").click();
        cy.get("a[data-action='active-filter-clear']").click();

        checkResults("rga-variant-view");

        let variantId;
        // region
        getResult("rga-variant-view", 0).then(vId => {
            variantId = vId;
            const region = variantId.trim().match(/\d+:\d+/)[0];
            cy.get("region-filter textarea").type(region);
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-variant-view");
            getResult("rga-variant-view", 0).then($resultCell => {
                cy.wrap($resultCell).should("contain", region);

            });
        });

        cy.get("opencga-active-filters button[data-filter-name='region']").click();
        checkResults("rga-variant-view");

        // gene Name
        // queries for the first gene and then check if the first result contains the gene.
        let geneName;
        getResult("rga-variant-view", 1).then($text => {
            geneName = $text.split(",")[0];
            cy.get("feature-filter input[type='text']").type(geneName);
            cy.get("feature-filter ul.dropdown-menu li").should("have.lengthOf", 1);

            // occasionally fails
            // Timed out retrying after 4050ms: cy.click() failed because this element is detached from the DOM.
            // DMKN
            cy.wait(500);
            cy.get("feature-filter ul.dropdown-menu li.active a").click({force: true});
            cy.get("div.search-button-wrapper button").click();

            checkResults("rga-variant-view");
            getResult("rga-variant-view", 1).then($resultCell => {
                cy.wrap($resultCell).should("contain", geneName);
            });
        });

        cy.get("opencga-active-filters button[data-filter-name='geneName']").click();
        checkResults("rga-variant-view");

        // detail-tabs allele pairs table
        cy.get("rga-variant-view detail-tabs ul.nav-tabs > li > a").contains("Allele Pairs").click();
        checkResults("rga-variant-allele-pairs");
        getResult("rga-variant-allele-pairs", 0).then($resultCell => {
            // check the variant id stored before
            cy.wrap($resultCell).should("contain", variantId);
        });

        // detail-tabs Individuals table
        // 1. get the first individual out of Individual View
        // 2. use it to filter Variant View
        // 3. check individual to be present in variant-individual-grid
        cy.get("button[data-tab-id='individual-tab']", {timeout: TIMEOUT}).click({force: true});
        checkResults("rga-individual-view");
        let IndividualId;
        getResult("rga-individual-view", 0).then($text => {
            IndividualId = $text;
            cy.get("button[data-tab-id='variant-tab']", {timeout: TIMEOUT}).click({force: true});
            checkResults("rga-variant-view");
            cy.get("div[data-cy='individualId'] input[type='text']").type(IndividualId + "{enter}");
            cy.get("div.search-button-wrapper button").click();
            checkResults("rga-variant-view");

            checkResults("rga-variant-individual-grid");
            getResult("rga-variant-individual-grid", 0).then($resultCell => {
                cy.wrap($resultCell).should("contain", IndividualId);
            });
        });
    });
});
<|endoftext|>


<commit_before>'use strict';
const _ = require('lodash');
const rp = require('request-promise');
const LocalRunner = require('./LocalRunner.js');
const DockerRunner = require('./DockerRunner.js');
const endpointToUrl = require('./common.js').endpointToUrl;
const WAIT_TIMEOUT = 400; // seconds

function sleep(ms) {
  return new Promise(resolve => setTimeout(() => resolve(), ms));
}

class InstanceManager {
  constructor(name) {
    this.instances = [];

    if (process.env.RESILIENCE_ARANGO_BASEPATH) {
      this.runner = new LocalRunner(process.env.RESILIENCE_ARANGO_BASEPATH);
    } else if (process.env.RESILIENCE_DOCKER_IMAGE) {
      this.runner = new DockerRunner(process.env.RESILIENCE_DOCKER_IMAGE);
    }

    if (process.env.ARANGO_STORAGE_ENGINE) {
      this.storageEngine = process.env.ARANGO_STORAGE_ENGINE;
      if (this.storageEngine !== 'rocksdb') {
        this.storageEngine = 'mmfiles';
      }
    } else {
      this.storageEngine = 'mmfiles';
    }

    if (!this.runner) {
      throw new Error(
        'Must specify RESILIENCE_ARANGO_BASEPATH (source root dir including a "build" folder containing compiled binaries or RESILIENCE_DOCKER_IMAGE to test a docker container'
      );
    }
    this.currentLog = '';
    this.agentCounter = 0;
    this.coordinatorCounter = 0;
    this.dbServerCounter = 0;
  }

  startArango(name, endpoint, role, args) {
    args.push('--server.authentication=false');
    //args.push('--log.level=v8=debug')
    //args.push('--log.level=communication=debug');
    args.push('--server.storage-engine=' + this.storageEngine);

    if (process.env.ARANGO_EXTRA_ARGS) {
      args = args.concat(process.env.ARANGO_EXTRA_ARGS.split(' '));
    }

    let instance = {
      name,
      role,
      process: null,
      status: 'NEW',
      exitcode: null,
      endpoint,
      args,
      logFn: line => {
        if (line.trim().length > 0) {
          let logLine =
            instance.name + '(' + instance.process.pid + '): \t' + line;
          if (process.env.LOG_IMMEDIATE && process.env.LOG_IMMEDIATE == "1") {
            console.log(logLine);
          } else {
            logLine = logLine.replace(/\x1B/g, '');
            this.currentLog += logLine + '\n';
          }
        }
      }
    };
    return this.runner.firstStart(instance);
  }

  startDbServer(name) {
    this.dbServerCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=PRIMARY',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'primary', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  getAgencyEndpoint() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    })[0].endpoint;
  }

  startCoordinator(name) {
    this.coordinatorCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=COORDINATOR',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'coordinator', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  replace(instance) {
    let name, role;
    switch (instance.role) {
      case 'coordinator':
        this.coordinatorCounter++;
        name = 'coordinator-' + this.coordinatorCounter;
        role = 'COORDINATOR';
        break;
      case 'primary':
        this.dbServerCounter++;
        name = 'dbServer-' + this.dbServerCounter;
        role = 'PRIMARY';
        break;
      default:
        throw new Error('Can only replace coordinators/dbServers');
    }
    if (this.instances.includes(instance)) {
      throw new Error('Instance must be destroyed before it can be replaced');
    }
    let args = [
      '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
      '--cluster.my-role=' + role,
      '--cluster.my-local-info=' + name,
      '--cluster.my-address=' + instance.endpoint
    ];
    return this.startArango(name, instance.endpoint, instance.role, args)
      .then(instance => this.waitForInstance(instance))
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  startAgency(options = {}) {
    let size = options.agencySize || 1;
    if (options.agencyWaitForSync === undefined) {
      options.agencyWaitForSync = false;
    }
    const wfs = options.agencyWaitForSync;
    let promise = Promise.resolve([]);
    let compactionStep = "200";
    let compactionKeep = "100";
    if (process.env.AGENCY_COMPACTION_STEP) {
      compactionStep = process.env.AGENCY_COMPACTION_STEP;
    }
    if (process.env.AGENCY_COMPACTION_KEEP) {
      compactionKeep = process.env.AGENCY_COMPACTION_KEEP;
    }
    for (var i = 0; i < size; i++) {
      promise = promise.then(instances => {
        return this.runner
          .createEndpoint()
          .then(endpoint => {
            let args = [
              '--agency.activate=true',
              '--agency.size=' + size,
              '--agency.pool-size=' + size,
              '--agency.wait-for-sync=true',
              '--agency.supervision=true',
              '--server.threads=16',
              '--agency.supervision-frequency=0.5',
              '--agency.supervision-grace-period=2.5',
              '--agency.compaction-step-size='+compactionStep,
              '--agency.compaction-keep-size='+compactionKeep,
              '--agency.my-address=' + endpoint,
              '--log.force-direct=true'
            ];
            if (instances.length === 0) {
              args.push('--agency.endpoint=' + endpoint);
            } else {
              args.push('--agency.endpoint=' + instances[0].endpoint);
            }
            this.agentCounter++;
            return this.startArango(
              'agency-' + this.agentCounter,
              endpoint,
              'agent',
              args
            );
          })
          .then(instance => {
            return [...instances, instance];
          });
      });
    }
    return promise.then(agents => {
      this.instances = agents;
      return agents;
    });
  }

  findPrimaryDbServer(collectionName) {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    return rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        [
          '/arango/Plan/Collections/_system',
          '/arango/Current/ServersRegistered'
        ]
      ]
    }).then(([info]) => {
      const collections = info.arango.Plan.Collections._system;
      const servers = info.arango.Current.ServersRegistered;
      for (const id of Object.keys(collections)) {
        const collection = collections[id];
        if (collection.name !== collectionName) {
          continue;
        }
        const shards = collection.shards;
        const shardsId = Object.keys(shards)[0];
        const uuid = shards[shardsId][0];
        const endpoint = servers[uuid].endpoint;
        const dbServers = this.dbServers().filter(
          instance => instance.endpoint === endpoint
        );
        if (!dbServers.length) {
          return Promise.reject(new Error(`Unknown endpoint "${endpoint}"`));
        }
        return dbServers[0];
      }
      return Promise.reject(
        new Error(`Unknown collection "${collectionName}"`)
      );
    });
  }

  startCluster(numAgents, numCoordinators, numDbServers, options = {}) {
    let agencyOptions = options.agents || {};
    _.extend(agencyOptions, {agencySize: numAgents});

    return this.startAgency(agencyOptions)
      .then(agents => {
        let promises = [Promise.resolve(agents)];

        let coordinators = Array.from(
          Array(numCoordinators).keys()
        ).map(index => {
          return this.startCoordinator('coordinator-' + (index + 1));
        });
        let dbServers = Array.from(Array(numDbServers).keys()).map(index => {
          return this.startDbServer('dbServer-' + (index + 1));
        });
        return Promise.all([].concat(coordinators, dbServers));
      })
      .then(servers => {
        return this.waitForAllInstances();
      })
      .then(() => {
        return this.getEndpoint();
      });
  }

  async waitForInstance(instance, started = Date.now()) {
    if (instance.status !== 'RUNNING') {
      throw new Error(`Instance ${instance.name} is down!`);
    }
    if (Date.now() - started > WAIT_TIMEOUT * 1000) {
      throw new Error(
        `Instance ${instance.name} is still not ready after ${WAIT_TIMEOUT} secs`
      );
    }

    let ok = false;
    try {
      await rp({
        method: 'GET',
        uri: endpointToUrl(instance.endpoint) + '/_api/version'
      });
      ok = true;
    } catch (e) {}
    if (ok) {
      return instance;
    } else {
      await sleep(100);
      return this.waitForInstance(instance, started);
    }
  }

  waitForAllInstances() {
    return Promise.all(
      this.instances.map(instance => {
        return this.waitForInstance(instance);
      })
    );
  }

  getEndpoint(instance) {
    return (instance || this.coordinators()[0]).endpoint;
  }

  getEndpointUrl(instance) {
    return endpointToUrl(this.getEndpoint(instance));
  }

  check() {
    return this.instances.every(instance => {
      return instance.status === 'RUNNING';
    });
  }

  shutdownCluster() {
    let shutdownPromise;

    let nonAgents = [].concat(this.coordinators(), this.dbServers());

    return Promise.all(nonAgents.map(this.shutdown.bind(this)))
    .then(() => {
      return Promise.all(this.agents().map(this.shutdown.bind(this)))
    });
  }

  cleanup() {
    return this.shutdownCluster()
      .then(() => {
        this.instances = [];
        this.agentCounter = 0;
        this.coordinatorCounter = 0;
        this.dbServerCounter = 0;
        return this.runner.cleanup();
      })
      .then(() => {
        let log = this.currentLog;
        this.currentLog = '';
        return log;
      });
  }

  dbServers() {
    return this.instances.filter(instance => {
      return instance.role === 'primary';
    });
  }

  coordinators() {
    return this.instances.filter(instance => {
      return instance.role === 'coordinator';
    });
  }

  agents() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    });
  }

  assignNewEndpoint(instance) {
    let index = this.instances.indexOf(instance);
    if (index === -1) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    var createNewEndpoint = () => {
      return this.runner.createEndpoint().then(endpoint => {
        if (endpoint != instance.endpoint) {
          return endpoint;
        } else {
          return createNewEndpoint();
        }
      });
    };

    return createNewEndpoint().then(endpoint => {
      [
        'server.endpoint',
        'agency.my-address',
        'cluster.my-address'
      ].filter(arg => {
        index = instance.args.indexOf('--' + arg + '=' + instance.endpoint);
        if (index !== -1) {
          instance.args[index] = '--' + arg + '=' + endpoint;
        }
      });
      return this.runner.updateEndpoint(instance, endpoint);
    });
  }

  // beware! signals are not supported on windows and it will simply do hard kills all the time
  // use shutdown to gracefully stop an instance!
  kill(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    instance.process.kill('SIGKILL');
    instance.status = 'KILLED';
    return new Promise((resolve, reject) => {
      let check = function() {
        if (instance.status !== 'EXITED') {
          setTimeout(check, 50);
        } else {
          resolve();
        }
      };
      check();
    });
  }

  shutdown(instance) {
    if (instance.status == 'EXITED') {
      return Promise.resolve(instance);
    }

    let checkDown = function() {
      return new Promise((resolve, reject) => {
        let attempts = 0;
        let maxAttempts = 360;  // 180s, note that the cluster internally
                                // has a 120s timeout
        let waitInterval = 500;
        (function innerCheckDown() {
          if (instance.status == 'EXITED') {
            resolve(instance);
          } else if (++attempts > maxAttempts) {
            reject(new Error(instance.name + ' did not stop gracefully after ' + (waitInterval * attempts) + 'ms'));
          } else {
            setTimeout(innerCheckDown, waitInterval);
          }
        })();
      });
    }

    return rp.delete({
      url: this.getEndpointUrl(instance) + '/_admin/shutdown',
    })
    .catch(err => {
      if (err && (err.statusCode === 503 || err.error)) {
        if (err.error.code == 'ECONNREFUSED') {
          console.warn('hmmm...server ' + instance.name + ' did not respond (' + err.code + '). Assuming it is dead. Status is: ' + instance.status);
          return checkDown();
        } else if (err.error.code == 'ECONNRESET') {
          return checkDown();
        } else if (err.statusCode === 503) {
          console.warn('server ' + instance.name + ' answered 503. Assuming it is shutting down. Status is: ' + instance.status);
          return checkDown();
        }
      }
      console.error("Unhandled error", err);

      return Promise.reject(err);
    })
    .then(x => {
      return checkDown();
    })
  }

  destroy(instance) {
    let promise;
    if (this.instances.includes(instance)) {
      promise = this.shutdown(instance);
    } else {
      promise = Promise.resolve();
    }
    return promise.then(() => this.runner.destroy(instance)).then(() => {
      const i = this.instances.indexOf(instance);
      if (i !== -1) {
        this.instances = [
          ...this.instances.slice(0, i),
          ...this.instances.slice(i + 1)
        ];
      }
    });
  }

  restart(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    return this.runner.restart(instance).then(() => {
      return this.waitForInstance(instance);
    });
  }

  // this will append the logs to the test in case of a failure so
  // you get a nice combined log of what happened on the server and client
  moveServerLogs(test) {
    if (test.state === 'failed') {
      test.err.message = this.currentLog + '\n\n' + test.err.message;
    }
    this.currentLog = '';
  }

  async getFoxxmaster() {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    const [info] = await rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        ['/arango/Current/Foxxmaster', '/arango/Current/ServersRegistered']
      ]
    });
    const uuid = info.arango.Current.Foxxmaster;
    const endpoint = info.arango.Current.ServersRegistered[uuid].endpoint;
    return this.instances.find(instance => instance.endpoint === endpoint);
  }

  async restartCluster() {
    const fm = await this.getFoxxmaster();
    await this.shutdownCluster();
    await Promise.all(this.agents().map(agent => this.restart(agent)));
    await Promise.all(this.dbServers().map(dbs => this.restart(dbs)));
    this.restart(fm);
    await sleep(100);
    await Promise.all(
      this.coordinators()
        .filter(coord => coord !== fm)
        .map(coord => this.restart(coord))
    );
    await this.waitForAllInstances();
  }
}

module.exports = InstanceManager;
<commit_msg>jenkins test<commit_after>'use strict';
const _ = require('lodash');
const rp = require('request-promise');
const LocalRunner = require('./LocalRunner.js');
const DockerRunner = require('./DockerRunner.js');
const endpointToUrl = require('./common.js').endpointToUrl;
const WAIT_TIMEOUT = 400; // seconds

function sleep(ms) {
  return new Promise(resolve => setTimeout(() => resolve(), ms));
}

class InstanceManager {
  constructor(name) {
    this.instances = [];

    if (process.env.RESILIENCE_ARANGO_BASEPATH) {
      this.runner = new LocalRunner(process.env.RESILIENCE_ARANGO_BASEPATH);
    } else if (process.env.RESILIENCE_DOCKER_IMAGE) {
      this.runner = new DockerRunner(process.env.RESILIENCE_DOCKER_IMAGE);
    }

    if (process.env.ARANGO_STORAGE_ENGINE) {
      this.storageEngine = process.env.ARANGO_STORAGE_ENGINE;
      if (this.storageEngine !== 'rocksdb') {
        this.storageEngine = 'mmfiles';
      }
    } else {
      this.storageEngine = 'mmfiles';
    }

    if (!this.runner) {
      throw new Error(
        'Must specify RESILIENCE_ARANGO_BASEPATH (source root dir including a "build" folder containing compiled binaries or RESILIENCE_DOCKER_IMAGE to test a docker container'
      );
    }
    this.currentLog = '';
    this.agentCounter = 0;
    this.coordinatorCounter = 0;
    this.dbServerCounter = 0;
  }

  startArango(name, endpoint, role, args) {
    args.push('--server.authentication=false');
    //args.push('--log.level=v8=debug')
    //args.push('--log.level=communication=debug');
    args.push('--server.storage-engine=' + this.storageEngine);

    if (process.env.ARANGO_EXTRA_ARGS) {
      args = args.concat(process.env.ARANGO_EXTRA_ARGS.split(' '));
    }

    let instance = {
      name,
      role,
      process: null,
      status: 'NEW',
      exitcode: null,
      endpoint,
      args,
      logFn: line => {
        if (line.trim().length > 0) {
          let logLine =
            instance.name + '(' + instance.process.pid + '): \t' + line;
          if (process.env.LOG_IMMEDIATE && process.env.LOG_IMMEDIATE == "1") {
            console.log(logLine);
          } else {
            logLine = logLine.replace(/\x1B/g, '');
            this.currentLog += logLine + '\n';
          }
        }
      }
    };
    return this.runner.firstStart(instance);
  }

  startDbServer(name) {
    this.dbServerCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=PRIMARY',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'primary', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  getAgencyEndpoint() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    })[0].endpoint;
  }

  startCoordinator(name) {
    this.coordinatorCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=COORDINATOR',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'coordinator', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  replace(instance) {
    let name, role;
    switch (instance.role) {
      case 'coordinator':
        this.coordinatorCounter++;
        name = 'coordinator-' + this.coordinatorCounter;
        role = 'COORDINATOR';
        break;
      case 'primary':
        this.dbServerCounter++;
        name = 'dbServer-' + this.dbServerCounter;
        role = 'PRIMARY';
        break;
      default:
        throw new Error('Can only replace coordinators/dbServers');
    }
    if (this.instances.includes(instance)) {
      throw new Error('Instance must be destroyed before it can be replaced');
    }
    let args = [
      '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
      '--cluster.my-role=' + role,
      '--cluster.my-local-info=' + name,
      '--cluster.my-address=' + instance.endpoint
    ];
    return this.startArango(name, instance.endpoint, instance.role, args)
      .then(instance => this.waitForInstance(instance))
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  startAgency(options = {}) {
    let size = options.agencySize || 1;
    if (options.agencyWaitForSync === undefined) {
      options.agencyWaitForSync = false;
    }
    const wfs = options.agencyWaitForSync;
    let promise = Promise.resolve([]);
    let compactionStep = "200";
    let compactionKeep = "100";
    if (process.env.AGENCY_COMPACTION_STEP) {
      compactionStep = process.env.AGENCY_COMPACTION_STEP;
    }
    if (process.env.AGENCY_COMPACTION_KEEP) {
      compactionKeep = process.env.AGENCY_COMPACTION_KEEP;
    }
    for (var i = 0; i < size; i++) {
      promise = promise.then(instances => {
        return this.runner
          .createEndpoint()
          .then(endpoint => {
            let args = [
              '--agency.activate=true',
              '--agency.size=' + size,
              '--agency.pool-size=' + size,
              '--agency.wait-for-sync=true',
              '--agency.supervision=true',
              '--server.threads=16',
              '--agency.supervision-frequency=0.5',
              '--agency.supervision-grace-period=2.5',
              '--agency.compaction-step-size='+compactionStep,
              '--agency.compaction-keep-size='+compactionKeep,
              '--agency.my-address=' + endpoint,
              '--log.force-direct=true'
            ];
            if (instances.length === 0) {
              args.push('--agency.endpoint=' + endpoint);
            } else {
              args.push('--agency.endpoint=' + instances[0].endpoint);
            }
            this.agentCounter++;
            return this.startArango(
              'agency-' + this.agentCounter,
              endpoint,
              'agent',
              args
            );
          })
          .then(instance => {
            return [...instances, instance];
          });
      });
    }
    return promise.then(agents => {
      this.instances = agents;
      return agents;
    });
  }

  findPrimaryDbServer(collectionName) {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    return rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        [
          '/arango/Plan/Collections/_system',
          '/arango/Current/ServersRegistered'
        ]
      ]
    }).then(([info]) => {
      const collections = info.arango.Plan.Collections._system;
      const servers = info.arango.Current.ServersRegistered;
      for (const id of Object.keys(collections)) {
        const collection = collections[id];
        if (collection.name !== collectionName) {
          continue;
        }
        const shards = collection.shards;
        const shardsId = Object.keys(shards)[0];
        const uuid = shards[shardsId][0];
        const endpoint = servers[uuid].endpoint;
        const dbServers = this.dbServers().filter(
          instance => instance.endpoint === endpoint
        );
        if (!dbServers.length) {
          return Promise.reject(new Error(`Unknown endpoint "${endpoint}"`));
        }
        return dbServers[0];
      }
      return Promise.reject(
        new Error(`Unknown collection "${collectionName}"`)
      );
    });
  }

  startCluster(numAgents, numCoordinators, numDbServers, options = {}) {
    let agencyOptions = options.agents || {};
    _.extend(agencyOptions, {agencySize: numAgents});

    return this.startAgency(agencyOptions)
      .then(agents => {
        let promises = [Promise.resolve(agents)];

        let coordinators = Array.from(
          Array(numCoordinators).keys()
        ).map(index => {
          return this.startCoordinator('coordinator-' + (index + 1));
        });
        let dbServers = Array.from(Array(numDbServers).keys()).map(index => {
          return this.startDbServer('dbServer-' + (index + 1));
        });
        return Promise.all([].concat(coordinators, dbServers));
      })
      .then(servers => {
        return this.waitForAllInstances();
      })
      .then(() => {
        return this.getEndpoint();
      });
  }

  async waitForInstance(instance, started = Date.now()) {
    if (instance.status !== 'RUNNING') {
      throw new Error(`Instance ${instance.name} is down!`);
    }
    if (Date.now() - started > WAIT_TIMEOUT * 1000) {
      throw new Error(
        `Instance ${instance.name} is still not ready after ${WAIT_TIMEOUT} secs`
      );
    }

    let ok = false;
    try {
      await rp({
        method: 'GET',
        uri: endpointToUrl(instance.endpoint) + '/_api/version'
      });
      ok = true;
    } catch (e) {}
    if (ok) {
      return instance;
    } else {
      await sleep(100);
      return this.waitForInstance(instance, started);
    }
  }

  waitForAllInstances() {
    return Promise.all(
      this.instances.map(instance => {
        return this.waitForInstance(instance);
      })
    );
  }

  getEndpoint(instance) {
    return (instance || this.coordinators()[0]).endpoint;
  }

  getEndpointUrl(instance) {
    return endpointToUrl(this.getEndpoint(instance));
  }

  check() {
    return this.instances.every(instance => {
      return instance.status === 'RUNNING';
    });
  }

  shutdownCluster() {
    let shutdownPromise;

    let nonAgents = [].concat(this.coordinators(), this.dbServers());

    return Promise.all(nonAgents.map(this.shutdown.bind(this)))
    .then(() => {
      return Promise.all(this.agents().map(this.shutdown.bind(this)))
    });
  }

  cleanup() {
    return this.shutdownCluster()
      .then(() => {
        this.instances = [];
        this.agentCounter = 0;
        this.coordinatorCounter = 0;
        this.dbServerCounter = 0;
        return this.runner.cleanup();
      })
      .then(() => {
        let log = this.currentLog;
        this.currentLog = '';
        return log;
      });
  }

  dbServers() {
    return this.instances.filter(instance => {
      return instance.role === 'primary';
    });
  }

  coordinators() {
    return this.instances.filter(instance => {
      return instance.role === 'coordinator';
    });
  }

  agents() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    });
  }

  assignNewEndpoint(instance) {
    let index = this.instances.indexOf(instance);
    if (index === -1) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    var createNewEndpoint = () => {
      return this.runner.createEndpoint().then(endpoint => {
        if (endpoint != instance.endpoint) {
          return endpoint;
        } else {
          return createNewEndpoint();
        }
      });
    };

    return createNewEndpoint().then(endpoint => {
      [
        'server.endpoint',
        'agency.my-address',
        'cluster.my-address'
      ].filter(arg => {
        index = instance.args.indexOf('--' + arg + '=' + instance.endpoint);
        if (index !== -1) {
          instance.args[index] = '--' + arg + '=' + endpoint;
        }
      });
      return this.runner.updateEndpoint(instance, endpoint);
    });
  }

  // beware! signals are not supported on windows and it will simply do hard kills all the time
  // use shutdown to gracefully stop an instance!
  kill(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    instance.process.kill('SIGKILL');
    instance.status = 'KILLED';
    return new Promise((resolve, reject) => {
      let check = function() {
        if (instance.status !== 'EXITED') {
          setTimeout(check, 50);
        } else {
          resolve();
        }
      };
      check();
    });
  }

  shutdown(instance) {
    if (instance.status == 'EXITED') {
      return Promise.resolve(instance);
    }

    let checkDown = function() {
      return new Promise((resolve, reject) => {
        let attempts = 0;
        let maxAttempts = 480;  // 240s, note that the cluster internally
                                // has a 120s timeout
        let waitInterval = 500;
        (function innerCheckDown() {
          if (instance.status == 'EXITED') {
            resolve(instance);
          } else if (++attempts > maxAttempts) {
            reject(new Error(instance.name + ' did not stop gracefully after ' + (waitInterval * attempts) + 'ms'));
          } else {
            setTimeout(innerCheckDown, waitInterval);
          }
        })();
      });
    }

    return rp.delete({
      url: this.getEndpointUrl(instance) + '/_admin/shutdown',
    })
    .catch(err => {
      if (err && (err.statusCode === 503 || err.error)) {
        if (err.error.code == 'ECONNREFUSED') {
          console.warn('hmmm...server ' + instance.name + ' did not respond (' + err.code + '). Assuming it is dead. Status is: ' + instance.status);
          return checkDown();
        } else if (err.error.code == 'ECONNRESET') {
          return checkDown();
        } else if (err.statusCode === 503) {
          console.warn('server ' + instance.name + ' answered 503. Assuming it is shutting down. Status is: ' + instance.status);
          return checkDown();
        }
      }
      console.error("Unhandled error", err);

      return Promise.reject(err);
    })
    .then(x => {
      return checkDown();
    })
  }

  destroy(instance) {
    let promise;
    if (this.instances.includes(instance)) {
      promise = this.shutdown(instance);
    } else {
      promise = Promise.resolve();
    }
    return promise.then(() => this.runner.destroy(instance)).then(() => {
      const i = this.instances.indexOf(instance);
      if (i !== -1) {
        this.instances = [
          ...this.instances.slice(0, i),
          ...this.instances.slice(i + 1)
        ];
      }
    });
  }

  restart(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    return this.runner.restart(instance).then(() => {
      return this.waitForInstance(instance);
    });
  }

  // this will append the logs to the test in case of a failure so
  // you get a nice combined log of what happened on the server and client
  moveServerLogs(test) {
    if (test.state === 'failed') {
      test.err.message = this.currentLog + '\n\n' + test.err.message;
    }
    this.currentLog = '';
  }

  async getFoxxmaster() {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    const [info] = await rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        ['/arango/Current/Foxxmaster', '/arango/Current/ServersRegistered']
      ]
    });
    const uuid = info.arango.Current.Foxxmaster;
    const endpoint = info.arango.Current.ServersRegistered[uuid].endpoint;
    return this.instances.find(instance => instance.endpoint === endpoint);
  }

  async restartCluster() {
    const fm = await this.getFoxxmaster();
    await this.shutdownCluster();
    await Promise.all(this.agents().map(agent => this.restart(agent)));
    await Promise.all(this.dbServers().map(dbs => this.restart(dbs)));
    this.restart(fm);
    await sleep(100);
    await Promise.all(
      this.coordinators()
        .filter(coord => coord !== fm)
        .map(coord => this.restart(coord))
    );
    await this.waitForAllInstances();
  }
}

module.exports = InstanceManager;
<|endoftext|>


<commit_before>!function (window) {
  function findPosts(document) {
    var tagName = 'div',
        className = 'post-body',
        querySelectorAll = 'querySelectorAll',
        getElementsByClassName = 'getElementsByClassName',
        getElementsByTagName = 'getElementsByTagName',
        div, list, i, length;
    if (document[querySelectorAll]) {
      list = document[querySelectorAll]([tagName, className].join('.'));
    } else if (document[getElementsByClassName]) {
      list = document[getElementsByClassName](className);
    } else {
      for(
        div = document[getElementsByTagName]('div'),
        list = [],
        i = 0,
        length = div.length;
        i < length; i++
      ) {
        if (~div[i].className.indexOf(className)) {
          list.push(div[i]);
        }
      }
    }
    for(
      i = 0,
      length = list.length;
      i < length; i++
    ) {
      list[i].innerHTML = tinydown(list[i].textContent || list[i].innerText);
    }
  }
  var done = false,
      add = window.addEventListener || window.attachEvent,
      parsePosts = function () {
        if (!done) {
          done = true;
          findPosts(window.document);
        }
      };
  add('DOMContentLoaded', parsePosts);
  add('onload', parsePosts);
  add('load', parsePosts);
}(this);<commit_msg>removed superflous file<commit_after><|endoftext|>


<commit_before>/*global contract, config, it, assert, web3*/
const SimpleStorage = require('Embark/contracts/SimpleStorage');
let accounts;
const {Utils} = require('Embark/EmbarkJS');

config({
  contracts: {
    deploy: {
      "SimpleStorage": {
        args: [100],
        onDeploy: (dependencies) => {
          return dependencies.contracts.SimpleStorage.methods.setRegistar(dependencies.contracts.SimpleStorage.options.address).send();
        }
      }
    }
  }
}, (err, theAccounts) => {
  accounts = theAccounts;
});

contract("SimpleStorage", function() {
  this.timeout(0);

  it("should set constructor value", async function() {
    let result = await SimpleStorage.methods.storedData().call();
    assert.strictEqual(parseInt(result, 10), 100);
  });

  it("set storage value", function(done) {
    Utils.secureSend(web3, SimpleStorage.methods.set(150), {}, false, async function(err) {
      if (err) {
        return done(err);
      }
      let result = await SimpleStorage.methods.get().call();
      assert.strictEqual(parseInt(result, 10), 150);
      done();
    });
  });

  it("should set to self address", async function() {
    let result = await SimpleStorage.methods.registar().call();
    assert.strictEqual(result, SimpleStorage.options.address);
  });

  it('should have the right defaultAccount', function() {
    assert.strictEqual(accounts[0], web3.eth.defaultAccount);
  });

  it("should alias contract address", function() {
    assert.strictEqual(SimpleStorage.options.address, SimpleStorage.address);
  });

  it('listens to events', function(done) {
    SimpleStorage.once('EventOnSet2', async function(error, result) {
      assert.strictEqual(error, null);
      assert.strictEqual(parseInt(result.returnValues.setValue, 10), 150);
      done(error);
    });

    SimpleStorage.methods.set2(150).send();
  });

  it('asserts event triggered', async function() {
    const tx = await SimpleStorage.methods.set2(160).send();
    assert.eventEmitted(tx, 'EventOnSet2', {passed: true, message: "hi", setValue: "160"});
  });

  it("should revert with a value lower than 5", async function() {
    await assert.reverts(SimpleStorage.methods.set3(2), {from: web3.eth.defaultAccount}, 'Returned error: VM Exception while processing transaction: revert Value needs to be higher than 5');
  });
});
<commit_msg>fix(@embark/tests): Fix failing test with `—node=embark`<commit_after>/*global contract, config, it, assert, web3*/
const SimpleStorage = require('Embark/contracts/SimpleStorage');
let accounts;
const {Utils} = require('Embark/EmbarkJS');

config({
  contracts: {
    deploy: {
      "SimpleStorage": {
        args: [100],
        onDeploy: (dependencies) => {
          return dependencies.contracts.SimpleStorage.methods.setRegistar(dependencies.contracts.SimpleStorage.options.address).send();
        }
      }
    }
  }
}, (err, theAccounts) => {
  accounts = theAccounts;
});

contract("SimpleStorage", function() {
  this.timeout(0);

  it("should set constructor value", async function() {
    let result = await SimpleStorage.methods.storedData().call();
    assert.strictEqual(parseInt(result, 10), 100);
  });

  it("set storage value", function(done) {
    Utils.secureSend(web3, SimpleStorage.methods.set(150), {}, false, async function(err) {
      if (err) {
        return done(err);
      }
      let result = await SimpleStorage.methods.get().call();
      assert.strictEqual(parseInt(result, 10), 150);
      done();
    });
  });

  it("should set to self address", async function() {
    let result = await SimpleStorage.methods.registar().call();
    assert.strictEqual(result, SimpleStorage.options.address);
  });

  it('should have the right defaultAccount', function() {
    assert.strictEqual(accounts[0], web3.eth.defaultAccount);
  });

  it("should alias contract address", function() {
    assert.strictEqual(SimpleStorage.options.address, SimpleStorage.address);
  });

  it('listens to events', async function() {
    const promise = new Promise((resolve) => {
      SimpleStorage.once("EventOnSet2", (error, result) => {
        assert.strictEqual(error, null);
        assert.strictEqual(parseInt(result.returnValues.setValue, 10), 150);
        resolve();
      });
    });

    await SimpleStorage.methods.set2(150).send();

    // execute the same method/value twice as a workaround for getting this test
    // to pass with --node=embark. The cause of the issue and how the workaround
    // works is still unknown.
    await SimpleStorage.methods.set2(150).send();

    return promise;
  });

  it('asserts event triggered', async function() {
    const tx = await SimpleStorage.methods.set2(160).send();
    assert.eventEmitted(tx, 'EventOnSet2', {passed: true, message: "hi", setValue: "160"});
  });

  it("should revert with a value lower than 5", async function() {
    await assert.reverts(SimpleStorage.methods.set3(2), {from: web3.eth.defaultAccount}, 'Returned error: VM Exception while processing transaction: revert Value needs to be higher than 5');
  });
});
<|endoftext|>


<commit_before>var data = [
      { y: '2013-02-24', a: 16},
      { y: '2013-09-15', a: 26},
      { y: '2013-10-02', a: 21},
      { y: '2014-01-01', a: 45},
      { y: '2015-01-01', a: 98},
      { y: '2015-06-01', a: 124},
      { y: '2015-07-01', a: 121},
      { y: '2016-01-01', a: 143},
      { y: '2016-02-01', a: 189},
    ],
    config = {
      data: data,
      xkey: 'y',
      ykeys: ['a'],
      labels: ['Twitter Followers'],
      fillOpacity: 1,
      hideHover: 'auto',
      behaveLikeLine: false,
      resize: true,
      lineColors:['#1da1f2'],
      axes: false,
      grid: false,
      smooth: false,
      pointSize: 0
  };
config.element = 'area-chart';
Morris.Area(config);
config.element = 'line-chart';
Morris.Line(config);
config.element = 'bar-chart';
Morris.Bar(config);
config.element = 'stacked';
config.stacked = true;
Morris.Bar(config);
Morris.Donut({
  element: 'pie-chart',
  data: [
    {label: "Friends", value: 30},
    {label: "Allies", value: 15},
    {label: "Enemies", value: 45},
    {label: "Neutral", value: 10}
  ]
});<commit_msg>twitter upd<commit_after>var data = [
      { y: '2013-02-24', a: 16},
      { y: '2013-09-15', a: 26},
      { y: '2013-10-02', a: 21},
      { y: '2014-01-01', a: 45},
      { y: '2015-01-01', a: 98},
      { y: '2015-06-01', a: 124},
      { y: '2015-07-01', a: 121},
      { y: '2016-01-01', a: 143},
      { y: '2017-02-01', a: 189},
      { y: '2017-02-01', a: 191}
    ],
    config = {
      data: data,
      xkey: 'y',
      ykeys: ['a'],
      labels: ['Twitter Followers'],
      fillOpacity: 1,
      hideHover: 'auto',
      behaveLikeLine: false,
      resize: true,
      lineColors:['#1da1f2'],
      axes: false,
      grid: false,
      smooth: false,
      pointSize: 0
  };
config.element = 'area-chart';
Morris.Area(config);
config.element = 'line-chart';
Morris.Line(config);
config.element = 'bar-chart';
Morris.Bar(config);
config.element = 'stacked';
config.stacked = true;
Morris.Bar(config);
Morris.Donut({
  element: 'pie-chart',
  data: [
    {label: "Friends", value: 30},
    {label: "Allies", value: 15},
    {label: "Enemies", value: 45},
    {label: "Neutral", value: 10}
  ]
});<|endoftext|>


<commit_before>(function ($) {
    $(function () {

        // Show or hide "Ajouter une nouvelle image" if current number of image is inferior to the max file limit
        function showHideDropzoneButton(id, nb, max) {
            if (nb < max) {
                $('div#' + id).parent().find('.dz-clickable').show();
            }
            else {
                $('div#' + id).parent().find('.dz-clickable').hide();
            }
        }

        // Re-write the saved input value
        function refreshDropzoneValue(resultInput, uploadedFiles) {

              // uploadedFiles is an array, so we use .join method to convert array key to string, with a tag separator #&#
              resultInput.val(uploadedFiles.join('#&#'));
        }

        $('.dropzone_widget').each(function (i, el) {
              Dropzone.autoDiscover = false;

              // Set vars
              var uploadedFiles = [];
              var dropzoneId = $(this).data('id');
              var dropzoneUri = $(this).data('url');
              var uploadMultiple = ($(this).data('multiple') > 0);

              // Is multiple upload allowed ?
              if (uploadMultiple)
                  var maxFile = $(this).data('max-file');
              else
                  var maxFile = 1;

              // Relative to the saved input
              var resultInput = $('#' + dropzoneId + ' input');

              var mediaDropzone = new Dropzone("div#" + dropzoneId, {
                  url: dropzoneUri,
                  maxFiles: maxFile,
                  uploadMultiple: uploadMultiple,
                  parallelUploads: 1,
                  maxFilesize: 10,
                  clickable: 'p.add-' + dropzoneId,
                  acceptedFiles: allowedMimeTypes,
                  addRemoveLinks: true,
                  dictDefaultMessage: '',
                  previewTemplate: $('div#' + dropzoneId).parent().find('.previewTemplateFileDrop').html(),
                  thumbnailWidth: "140",
                  thumbnailHeight: "93",
                  dictInvalidFileType: 'Mauvais type de fichier',
                  dictRemoveFile: '',
                  dictCancelUpload: '',
                  dictCancelUploadConfirmation: 'Confirmer',
                  dictMaxFilesExceeded: 'Limite de photo atteinte.',
                  dictFileTooBig: 'Le fichier est trop volumineux (max 10 Mo)',
                  init: function () {

                      // relative to DropZone
                      var $this = this;

                      // set MaxFiles to 1 or more function to uploadMultiple var
                      $this.options.maxFiles = maxFile;

                      // Load existing images for this okaz
                      var backup_folder = $('div#' + dropzoneId).parent().find('.dropzone-backup');

                      // S'il y a des images existantes au chargement de la page
                      if (backup_folder.length && backup_folder.has('img')) {

                          // Boucle sur chaque image existante
                          backup_folder.find('img').each(function (i, el) {

                              // On set une variable mockFile qui servira ensuite notemment pour le removeFile
                              var mockFile = {
                                name: "preview_"+$(this).data('key'),
                                id : $(this).data('key') // Used for removeFile method
                              };

                              // Get the file ID
                              var key = $(this).data('key');

                              // Let Dropzone know there are images
                              Dropzone.forElement('div#' + dropzoneId).emit("addedfile", mockFile);
                              Dropzone.forElement('div#' + dropzoneId).emit("thumbnail", mockFile, $(this).attr('src'));

                              // Complete the var 'files' with existing files
                              $this.files.push(mockFile);

                              // Set to the thumbnail container with the file ID in a data-rel attribut
                              mockFile.previewTemplate.setAttribute('data-rel', key);
                              $(mockFile.previewElement).find('.dz-label').text($(this).attr('data-label'));

                              // Complete the uploadedFiles with existing files for the resultInput
                              uploadedFiles.push(key);
                          });

                          // Show all the remove button above existing images
                          $('.dz-remove').show();
                      }

                      // Show clickable button if limit upload
                      if ($this.files.length < $this.options.maxFiles) {
                          $('div#' + dropzoneId).parent().find('.dz-clickable').show();
                      }
                      else {
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      }

                      // Show clickable button only if file length < to the maxFile limit
                      showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);

                      /* Create Waiting message */
                      if ($('#pictureLoader').length == 0) {
                          var loader = $('
', {
                              'id': 'pictureLoader',
                              'html': '

Merci de patienter pendant l\'upload de votre fichier...
'
                          }).insertAfter($('form .form-actions')).hide();
                      }

                      // -- @-@ --
                      // EVENT LISTENERS
                      // -- @-@ --
                      $this
                      .on("success", function (file, response) {

                          if(response[0]['error'] == false) {
                              file['id'] = response[0].id;

                              // Set to the thumbnail container with the file ID in a data-rel attribut
                              file.previewElement.setAttribute('data-rel', response[0].id);

                              // Hide progress bar
                              $(file.previewElement).find('.dz-progress').hide();

                              // Show remove button
                              $(file.previewElement).find('.dz-remove').show();

                              // Create an array with the new uploaded file
                              var newFile = [response[0].id];

                              if (response[0]['path'] != undefined && $(file.previewElement).find('img').attr('src') == undefined) {
                                  $(file.previewElement).find('img').attr('src', response[0]['path']);
                              }

                              if (response[0]['name'] != undefined) {
                                  $(file.previewElement).find('.dz-label').text(response[0]['name']);
                              }

                              // Merge new array with existing files
                              $.merge(uploadedFiles, newFile);

                              // Refresh input value
                              refreshDropzoneValue(resultInput, uploadedFiles);
                          } else {
                              Dropzone.forElement('div#' + dropzoneId).emit("error", file, response[0]['errorMessage']);
                          }
                      })

                      .on("removedfile", function (file) {
                          // Create a new array. It will use to re-write uploadedFiles array
                          var newData = [];

                          // Fill the newData array
                          $(uploadedFiles).each(function (i, el) {
                              if (el != file.id) {
                                  newData.push(uploadedFiles[i]);
                              }
                          });

                          // Set uploadedFiles
                          uploadedFiles = newData;

                          // Refresh input value
                          refreshDropzoneValue(resultInput, uploadedFiles);

                          // Show clickable button only if file length < to the maxFile limit
                          showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);
                      })

                      .on("complete", function (file) {
                          // Show clickable button only if file length < to the maxFile limit
                          showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);

                          // Show submit button when uploading is finished
                          $('form .form-actions').show();

                          // Hide loader
                          $('#pictureLoader').hide();
                      })

                      .on("maxfilesreached", function (file) {
                          // Hide "Ajouter une nouvelle image" button
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      })

                      .on("maxfilesexceeded", function (file) {
                          // Hide "Ajouter une nouvelle image" button
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      })

                      .on("error", function (file, message) {

                          alert(message);

                          // Remove Preview file && cancel upload
                          $this.removeFile(file);
                      })

                      .on("sending", function (file, xhr, formData) {
                          // Hide submit button when sending new file
                          $('form .form-actions').hide();

                          // Show loader loader
                          $('#pictureLoader').show();
                      })

                      .on("addedfile", function (file, xhr, formData) {

                          // Check if nb of files is always inferior to the max files allowed
                          if($this.files.length > $this.options.maxFiles) {
                              // Remove Preview file && cancel upload
                              this.removeFile(file);
                          }
                      });
                  }
          });

        });
    });
})(jQuery);
<commit_msg>Add events on success and error upload in media_widget.js<commit_after>(function ($) {
    $(function () {

        // Show or hide "Ajouter une nouvelle image" if current number of image is inferior to the max file limit
        function showHideDropzoneButton(id, nb, max) {
            if (nb < max) {
                $('div#' + id).parent().find('.dz-clickable').show();
            }
            else {
                $('div#' + id).parent().find('.dz-clickable').hide();
            }
        }

        // Re-write the saved input value
        function refreshDropzoneValue(resultInput, uploadedFiles) {

              // uploadedFiles is an array, so we use .join method to convert array key to string, with a tag separator #&#
              resultInput.val(uploadedFiles.join('#&#'));
        }

        $('.dropzone_widget').each(function (i, el) {
              Dropzone.autoDiscover = false;

              // Set vars
              var uploadedFiles = [];
              var dropzoneId = $(this).data('id');
              var dropzoneUri = $(this).data('url');
              var uploadMultiple = ($(this).data('multiple') > 0);

              // Is multiple upload allowed ?
              if (uploadMultiple)
                  var maxFile = $(this).data('max-file');
              else
                  var maxFile = 1;

              // Relative to the saved input
              var resultInput = $('#' + dropzoneId + ' input');

              var mediaDropzone = new Dropzone("div#" + dropzoneId, {
                  url: dropzoneUri,
                  maxFiles: maxFile,
                  uploadMultiple: uploadMultiple,
                  parallelUploads: 1,
                  maxFilesize: 10,
                  clickable: 'p.add-' + dropzoneId,
                  acceptedFiles: allowedMimeTypes,
                  addRemoveLinks: true,
                  dictDefaultMessage: '',
                  previewTemplate: $('div#' + dropzoneId).parent().find('.previewTemplateFileDrop').html(),
                  thumbnailWidth: "140",
                  thumbnailHeight: "93",
                  dictInvalidFileType: 'Mauvais type de fichier',
                  dictRemoveFile: '',
                  dictCancelUpload: '',
                  dictCancelUploadConfirmation: 'Confirmer',
                  dictMaxFilesExceeded: 'Limite de photo atteinte.',
                  dictFileTooBig: 'Le fichier est trop volumineux (max 10 Mo)',
                  init: function () {

                      // relative to DropZone
                      var $this = this;

                      // set MaxFiles to 1 or more function to uploadMultiple var
                      $this.options.maxFiles = maxFile;

                      // Load existing images for this okaz
                      var backup_folder = $('div#' + dropzoneId).parent().find('.dropzone-backup');

                      // S'il y a des images existantes au chargement de la page
                      if (backup_folder.length && backup_folder.has('img')) {

                          // Boucle sur chaque image existante
                          backup_folder.find('img').each(function (i, el) {

                              // On set une variable mockFile qui servira ensuite notemment pour le removeFile
                              var mockFile = {
                                name: "preview_"+$(this).data('key'),
                                id : $(this).data('key') // Used for removeFile method
                              };

                              // Get the file ID
                              var key = $(this).data('key');

                              // Let Dropzone know there are images
                              Dropzone.forElement('div#' + dropzoneId).emit("addedfile", mockFile);
                              Dropzone.forElement('div#' + dropzoneId).emit("thumbnail", mockFile, $(this).attr('src'));

                              // Complete the var 'files' with existing files
                              $this.files.push(mockFile);

                              // Set to the thumbnail container with the file ID in a data-rel attribut
                              mockFile.previewTemplate.setAttribute('data-rel', key);
                              $(mockFile.previewElement).find('.dz-label').text($(this).attr('data-label'));

                              // Complete the uploadedFiles with existing files for the resultInput
                              uploadedFiles.push(key);
                          });

                          // Show all the remove button above existing images
                          $('.dz-remove').show();
                      }

                      // Show clickable button if limit upload
                      if ($this.files.length < $this.options.maxFiles) {
                          $('div#' + dropzoneId).parent().find('.dz-clickable').show();
                      }
                      else {
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      }

                      // Show clickable button only if file length < to the maxFile limit
                      showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);

                      /* Create Waiting message */
                      if ($('#pictureLoader').length == 0) {
                          var loader = $('
', {
                              'id': 'pictureLoader',
                              'html': '

Merci de patienter pendant l\'upload de votre fichier...
'
                          }).insertAfter($('form .form-actions')).hide();
                      }

                      // -- @-@ --
                      // EVENT LISTENERS
                      // -- @-@ --
                      $this
                      .on("success", function (file, response) {

                          if(response[0]['error'] == false) {
                              $('div#' + dropzoneId).trigger('alpixel_media.dropzone.upload.pre.success', file);
                              file['id'] = response[0].id;

                              // Set to the thumbnail container with the file ID in a data-rel attribut
                              file.previewElement.setAttribute('data-rel', response[0].id);

                              // Hide progress bar
                              $(file.previewElement).find('.dz-progress').hide();

                              // Show remove button
                              $(file.previewElement).find('.dz-remove').show();

                              // Create an array with the new uploaded file
                              var newFile = [response[0].id];

                              if (response[0]['path'] != undefined && $(file.previewElement).find('img').attr('src') == undefined) {
                                  $(file.previewElement).find('img').attr('src', response[0]['path']);
                              }

                              if (response[0]['name'] != undefined) {
                                  $(file.previewElement).find('.dz-label').text(response[0]['name']);
                              }

                              // Merge new array with existing files
                              $.merge(uploadedFiles, newFile);

                              // Refresh input value
                              refreshDropzoneValue(resultInput, uploadedFiles);
                              $('div#' + dropzoneId).trigger('alpixel_media.dropzone.upload.post.success', file);
                          } else {
                              $('div#' + dropzoneId).trigger('alpixel_media.dropzone.upload.error', file);
                              Dropzone.forElement('div#' + dropzoneId).emit("error", file, response[0]['errorMessage']);
                          }
                      })

                      .on("removedfile", function (file) {
                          // Create a new array. It will use to re-write uploadedFiles array
                          var newData = [];

                          // Fill the newData array
                          $(uploadedFiles).each(function (i, el) {
                              if (el != file.id) {
                                  newData.push(uploadedFiles[i]);
                              }
                          });

                          // Set uploadedFiles
                          uploadedFiles = newData;

                          // Refresh input value
                          refreshDropzoneValue(resultInput, uploadedFiles);

                          // Show clickable button only if file length < to the maxFile limit
                          showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);
                      })

                      .on("complete", function (file) {
                          // Show clickable button only if file length < to the maxFile limit
                          showHideDropzoneButton(dropzoneId, $this.files.length, $this.options.maxFiles);

                          // Show submit button when uploading is finished
                          $('form .form-actions').show();

                          // Hide loader
                          $('#pictureLoader').hide();
                      })

                      .on("maxfilesreached", function (file) {
                          // Hide "Ajouter une nouvelle image" button
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      })

                      .on("maxfilesexceeded", function (file) {
                          // Hide "Ajouter une nouvelle image" button
                          $('div#' + dropzoneId).parent().find('.dz-clickable').hide();
                      })

                      .on("error", function (file, message) {

                          alert(message);

                          // Remove Preview file && cancel upload
                          $this.removeFile(file);
                      })

                      .on("sending", function (file, xhr, formData) {
                          // Hide submit button when sending new file
                          $('form .form-actions').hide();

                          // Show loader loader
                          $('#pictureLoader').show();
                      })

                      .on("addedfile", function (file, xhr, formData) {

                          // Check if nb of files is always inferior to the max files allowed
                          if($this.files.length > $this.options.maxFiles) {
                              // Remove Preview file && cancel upload
                              this.removeFile(file);
                          }
                      });
                  }
          });

        });
    });
})(jQuery);
<|endoftext|>