<commit_before>"use strict";
// Device
const EventEmitter = require('events');
const util = require('util');

function Device(options) {
  EventEmitter.call(this);
  this.signalType = "stream";          // default set to stream
  this.name = "undersigned_device";    // Used to designate the specific device
}
util.inherits(Device, EventEmitter);

// Static functions

Device.init = function () {
    turnOnDevice();
};

Device.terminate = function () {
    turnOffDevice();
};

var qrScanner = new Device({
    name: "qrScanner",
    type: "stream", // type of sensor
    init: function (q) {
      turnOnQRScanner(); // fake code that turns on physical hardware
    },
    on: function () {
      if (Scanning()) {
        return this.emit("stream", {
          status: "off"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "closed",
        error: "error message"
      });
    },
    off: function () {
      if (closeGate()) {
        return this.emit("switch", {
          name: this.name,
          status: "closed"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "open",
        error: "some error message"
      });
    },
    terminate: function () {
      turnOffGate();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }

});

var camera = new Device({
    name: "license Reader",
    type: "stream",
    init: function (q) {
        turnOnCamera(); //fake code that turns on physical camera
    },
    on: function (){
        if (qrSuccess()){
            return this.emit("stream",{}
        )};
        }
    },
    terminate: function(){
        turnOffCamera();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
});
camera.on('capture', function (stream) {
  logToTheMongoDb(stream.capture(1,4));
});

// Device Types
var depthSensor = new Device({

    name: "pr-sensor",
    meta: { depthThreshold: -4}
    type: "stream-trigger", // type of sensor
    init: function (q) {
      turnOnSensor(); // fake code that turns on physical hardware
    },
    on: function (deviceStream) {
      // on is dependent on the type of sensor you are using
      // for a stream trigger this will use difference
      var {newDepth, oldDepth}= marshall(deviceStream);
      var curDepth = newDepth - oldDepth;
      if (curDepth > this.depthThreshold) {
        this.emit("trigger", {
          deviceName: this.name,
          depth: curDepth
        });
      }
    },
    terminate: function () {
      turnOffDevice();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
});

var gate = new Device({
    name: "entance-gate",
    type: "switch", // type of sensor
    init: function (q) {
      turnOnGate(); // fake code that turns on physicall hardware
    },
    on: function () {
      if (openGate()) {
        return this.emit("switch", {
          status: "open"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "closed",
        error: "some error message"
      });
    },
    off: function () {
      if (closeGate()) {
        return this.emit("switch", {
          name: this.name,
          status: "closed"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "open",
        error: "some error message"
      });
    },
    terminate: function () {
      turnOffGate();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }

});

var terminal = new Device ({
    name: "ent-term",
    type: "visual-output",
    init: function (q) {
      turnOnTerminal(); // fake code that turns on physical hardware
    },
    on: function () {
      if (systemOn()) {
        return this.emit("switch", {
          status: "on"
        });
      }
    },
    off: function () {
      if (systemOff()) {
        return this.emit("switch", {
          name: this.name,
          status: "off"
        });
      }
    },
    terminate: function () {
      turnOffTerminal();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
}

// device controller that tells the garage how to sequetially operate devices
function controller() {
  on(eventName, optionMinObj, actionFunction)
  // when customer arrives, the arrival sequence begins
  // it begins when the entrance sensor is activated
  device.on('arrival-sequence')
    .where((obj) => { obj.name == "entrance-sensor"})
    .do((obj) => {
      // the terminal is activated to communicate with the customer
      device.name("entrance-terminal").init((error) => {
        if (error) throw "error message 1"
      });
    });
    // when ticket is taken by customer, the gate opens up
    device.on('open-gate')
      .where((obj) => {obj.name == "entrance-terminal"})
      .do((obj) => {
        device.name("entrance-gate").init((error) => {
          if (error) throw "error message 2"
        }
        device.name("entrance-gate").terminate());
      });
    // the gate sensor now makes sure the car has
    // cleared the entrance before closing the gate
    device.on('gate-pass')
      .where((obj) => {obj.name == "gate-sensor"})
      .do((obj) => {
        device.name("entrance-gate").init((error) => {
          if (error) throw "error message 3"
        });
      });
    device.on('activate-spot-sensor')
      .where((obj) => {obj.name == "garage-spot-sensor"})
      .do((obj) => {
        device.name("spot-sensor").init((error) =>
          if (error) throw "error message 4"
        });
      });
}
<commit_msg>added comments<commit_after>"use strict";
// Device
const EventEmitter = require('events');
const util = require('util');

function Device(options) {
  EventEmitter.call(this);
  this.signalType = "stream";          // default set to stream
  this.name = "undersigned_device";    // Used to designate the specific device
}
util.inherits(Device, EventEmitter);

// Static functions

Device.init = function () {
    turnOnDevice();
};

Device.terminate = function () {
    turnOffDevice();
};

var qrScanner = new Device({
    name: "qrScanner",
    type: "stream", // type of sensor
    init: function (q) {
      turnOnQRScanner(); // fake code that turns on physical hardware
    },
    on: function () {
      if (Scanning()) {
        return this.emit("stream", {
          status: "off"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "closed",
        error: "error message"
      });
    },
    off: function () {
      if (!Scanning()) {
        return this.emit("switch", {
          name: this.name,
          status: "closed"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "open",
        error: "some error message"
      });
    },
    terminate: function () {
      turnOffGate();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }

});

var camera = new Device({
    name: "license Reader",
    type: "stream",
    init: function (q) {
        turnOnCamera(); //fake code that turns on physical camera
    },
    on: function (){
        if (qrSuccess()){
            return this.emit("stream",{}
        )};
        }
    },
    readLicense(){
      licenseNumber = capture(license);
    }
    recordLicense(){
      logToTheMongoDb(licenseNumber);
    }
    terminate: function(){
      turnOffCamera();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
});
camera.on('capture', function (stream) {
  logToTheMongoDb(stream.capture(1,4));
});

var depthSensor = new Device({

    name: "pr-sensor",
    meta: { depthThreshold: -4}
    type: "stream-trigger", // type of sensor
    init: function (q) {
      turnOnSensor(); // fake code that turns on physical hardware
    },
    on: function (deviceStream) {
      // on is dependent on the type of sensor you are using
      // for a stream trigger this will use difference
      var {newDepth, oldDepth}= marshall(deviceStream);
      var curDepth = newDepth - oldDepth;
      if (curDepth > this.depthThreshold) {
        this.emit("trigger", {
          deviceName: this.name,
          depth: curDepth
        });
      }
    },
    terminate: function () {
      turnOffDevice();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
});

var gate = new Device({
    name: "entance-gate",
    type: "switch", // type of sensor
    init: function (q) {
      turnOnGate(); // fake code that turns on physicall hardware
    },
    on: function () {
      if (openGate()) {
        return this.emit("switch", {
          status: "open"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "closed",
        error: "some error message"
      });
    },
    off: function () {
      if (closeGate()) {
        return this.emit("switch", {
          name: this.name,
          status: "closed"
        });
      }

      return this.emit("switch", {
        name: this.name,
        status: "open",
        error: "some error message"
      });
    },
    terminate: function () {
      turnOffGate();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }

});

var terminal = new Device ({
    name: "ent-term",
    type: "visual-output",
    init: function (q) {
      turnOnTerminal(); // fake code that turns on physical hardware
    },
    on: function () {
      if (systemOn()) {
        return this.emit("switch", {
          status: "on"
        });
      }
    },
    off: function () {
      if (systemOff()) {
        return this.emit("switch", {
          name: this.name,
          status: "off"
        });
      }
    },
    terminate: function () {
      turnOffTerminal();
    },
    reboot: function () {
      this.terminate();
      this.init();
    }
}

// device controller that tells the garage how to sequetially operate devices
function controller() {
  on(eventName, optionMinObj, actionFunction)
  // when customer arrives, the arrival sequence begins
  // it begins when the entrance sensor is activated
  device.on('arrival-sequence')
    .where((obj) => { obj.name == "entrance-sensor"})
    .do((obj) => {
      // the terminal is activated to communicate with the customer
      device.name("entrance-terminal").init((error) => {
        if (error) throw "error message 1"
      });
    });
    // when ticket is taken by customer, the gate opens up
    device.on('open-gate')
      .where((obj) => {obj.name == "entrance-terminal"})
      .do((obj) => {
        device.name("entrance-gate").init((error) => {
          if (error) throw "error message 2"
        }
        device.name("entrance-gate").terminate());
      });
    // the gate sensor now makes sure the car has
    // cleared the entrance before closing the gate
    device.on('gate-pass')
      .where((obj) => {obj.name == "gate-sensor"})
      .do((obj) => {
        device.name("entrance-gate").init((error) => {
          if (error) throw "error message 3"
        });
      });
      // spot sensor should stay active until it becomes vacant
    device.on('activate-spot-sensor')
      .where((obj) => {obj.name == "garage-spot-sensor"})
      .do((obj) => {
        device.name("spot-sensor").init((error) =>
          if (error) throw "error message 4"
        });
      });
}

/*
sequence to write in controller:

check for error at every step

arrival-sequence    :

entrance-sensor
terminal screen on
user input (reservation info)
	calculate closest spot in garage
		add spot to ticket
    if reservation
		display reserved time and calculated spot
	else if walkin
		take ticket with qr code
	else
		return error message
gate open
car clears gate
	gate close
spot sensors on
if spot sensor sees car parked
spot sensors off

departure-sequence :

exit-sensor
terminal ask for ticket
user insert ticket
terminal send charge request
approval pass
exit gate open
exit gate close

devices and methods/attr

device

  gate
    entrance-gate{
      open()
      close()
    }

  sensor
    entrance-sensor{
      inRange() : boolean
    }
    gate sensor{
      inRange() : boolean
    }
    spot sensor{
      inRange() : boolean
    }

  camera
    license-plate-reader{
      read()
      record()
    }
  terminal
    entrance-terminal{

    }
    exit-terminal{
      requestTicket()
      displayFee()
    }

  qrScanner{
    on()
    emit()
    }

  ticket printer{
    printTicket()
  }

  ticket reader{
    readTicket()
  }

*/
<|endoftext|>


<commit_before>let moment = require('moment-timezone');
require('moment/locale/en-gb');
moment.locale("en-gb");

describe('Staff movements', function () {
  var userName = "Unknown";

  beforeEach(function () {
    var schDT = new Date().toISOString().split("T")[0];
    cy.deleteData()
      .addFlight(schDT + "T00:55:00Z", schDT + "T00:55:00Z", schDT + "T01:01:00Z", schDT + "T01:05:00Z", schDT + "T00:15:00Z");
  });

  function midnightThisMorning() {
    return moment().tz('Europe/London').startOf('day');
  }

  function shifts(numberOfStaff) {
    return {
      "shifts": [
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 15).toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 30).toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 45).toISOString() }
      ]
    };
  }

  describe('When adding staff movements on the desks and queues page', function () {
    it("Should update the available staff when 1 staff member is added for 1 hour", function () {
      cy
        .asABorderForcePlanningOfficer()
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .removeXMovements(1);
    });

    it("Should update the available staff when 1 staff member is removed for 1 hour", function () {
      cy
        .asABorderForcePlanningOfficer()
        .saveShifts(shifts(2))
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .removeMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(-1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .removeXMovements(1);
    });

    it("Should update the available staff when 1 staff member is added for 1 hour twice", function () {
      cy
        .asABorderForceOfficer()
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .findAndClick('Desks & Queues')
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(2)
        .checkStaffDeployedOnDesksAndQueuesTabAre(2)
        .checkStaffAvailableOnDesksAndQueuesTabAre(2)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(2)
        .checkUserNameOnMovementsTab(2, "Unknown")
        .removeXMovements(2);
    });
  });
});

Cypress.Commands.add('addMovementFor1HourAt', (numStaff, hour) => cy.addOrRemoveMovementFor1HourAt("add", numStaff, hour));

Cypress.Commands.add('removeMovementFor1HourAt', (numStaff, hour) => cy.addOrRemoveMovementFor1HourAt("remove", numStaff, hour));

Cypress.Commands.add('staffDeployedAtRow', (row) => {
  const selector = '#sticky-body > :nth-child(' + (row + 1) + ') > :nth-child(14)';
  cy.get(selector);
});

Cypress.Commands.add('staffMovementsAtRow', (row) => {
  const selector = 'td.non-pcp:nth($index)';
  cy.get(selector.replace('$index', row * 2 + 1));
});

Cypress.Commands.add('staffAvailableAtRow', (row) => {
  const selector = ':nth-child($index) > .staff-adjustments > :nth-child(1) > .deployed';
  cy.get(selector.replace('$index', row + 1));
});

Cypress.Commands.add('staffOverTheDayAtSlot', (slot) => {
  const selector = '#available-staff tbody > :nth-child(2) > :nth-child(' + (slot + 1) + ')';
  cy.get(selector);
});

Cypress.Commands.add('checkStaffAvailableOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1, 2, 3].map((row) => {
    cy.staffAvailableAtRow(row).contains(numStaff);
  });
  cy.staffAvailableAtRow(4, 0).contains(0);
});

Cypress.Commands.add('checkStaffMovementsOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1, 2, 3].map((row) => {
    cy.staffMovementsAtRow(row).contains(numStaff);
  });
  cy.staffMovementsAtRow(4).contains(0);
});

Cypress.Commands.add('checkStaffDeployedOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1, 2, 3].map((row) => {
    cy.staffDeployedAtRow(row).contains(numStaff);
  });
  cy.staffDeployedAtRow(4).contains(0);
});

Cypress.Commands.add('checkStaffNumbersOnMovementsTabAre', (numStaff) => {
  cy.contains("Staff Movements").click().then(() => {
    [0, 1, 2, 3].map((slot) => { cy.staffOverTheDayAtSlot(slot).contains(numStaff) });
    cy.staffOverTheDayAtSlot(4).contains("0");
  });
});

Cypress.Commands.add('removeXMovements', (numToRemove) => {
  cy.contains("Staff Movements").click().then(() => {
    for (let i = 1; i <= numToRemove; i++) {
      cy.get('.fa-remove').first().click().end();
      cy.get('.fa-remove').should('have.length', numToRemove - i).end();
    }
  });
});

Cypress.Commands.add('checkUserNameOnMovementsTab', (numMovements, userName) => {
  cy.get('.movement-display')
    .should('have.length', numMovements)
    .each(($element, index, $lis) => {
      cy.wrap($element).contains(userName);
      cy.wrap($element);
    });
});

Cypress.Commands.add('addOrRemoveMovementFor1HourAt', (addOrRemove, numStaff, hour) => {
  const buttonLabel = addOrRemove == "add" ? "+" : "-";
  const buttonNth = addOrRemove == "add" ? 3 : 1;

  cy.get('#sticky-body tr:nth-child(1) td').should('have.length', 15).then(() => {
    for (let i = 0; i < numStaff; i++) {
      cy.get('.staff-adjustments > :nth-child(' + (hour + 1) + ') > :nth-child(' + buttonNth + ')').contains(buttonLabel).then((el) => {
        el.click();
        cy.get('#staff-adjustment-dialogue').contains("Save").then((saveButton) => {
          saveButton.click();
        })
      });
    }
  });
});
<commit_msg>DRTII-27 Fix e2e staff movement tests<commit_after>let moment = require('moment-timezone');
require('moment/locale/en-gb');
moment.locale("en-gb");

describe('Staff movements', function () {
  var userName = "Unknown";

  beforeEach(function () {
    var schDT = new Date().toISOString().split("T")[0];
    cy.deleteData()
      .addFlight(schDT + "T00:55:00Z", schDT + "T00:55:00Z", schDT + "T01:01:00Z", schDT + "T01:05:00Z", schDT + "T00:15:00Z");
  });

  function midnightThisMorning() {
    return moment().tz('Europe/London').startOf('day');
  }

  function shifts(numberOfStaff) {
    return {
      "shifts": [
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 15).toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 30).toISOString() },
        { "port_code": "test", "terminal": "T1", "staff": String(numberOfStaff), "shift_start": midnightThisMorning().add('minute', 45).toISOString() }
      ]
    };
  }

  describe('When adding staff movements on the desks and queues page', function () {
    it("Should update the available staff when 1 staff member is added for 1 hour", function () {
      cy
        .asABorderForcePlanningOfficer()
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .removeXMovements(1);
    });

    it("Should update the available staff when 1 staff member is removed for 1 hour", function () {
      cy
        .asABorderForcePlanningOfficer()
        .saveShifts(shifts(2))
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .removeMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(-1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .removeXMovements(1);
    });

    it("Should update the available staff when 1 staff member is added for 1 hour twice", function () {
      cy
        .asABorderForceOfficer()
        .navigateHome()
        .navigateToMenuItem('T1')
        .choose24Hours()
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(1)
        .checkStaffDeployedOnDesksAndQueuesTabAre(1)
        .checkStaffAvailableOnDesksAndQueuesTabAre(1)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(1)
        .checkUserNameOnMovementsTab(1, "Unknown")
        .findAndClick('Desks & Queues')
        .addMovementFor1HourAt(1, 0)
        .checkStaffMovementsOnDesksAndQueuesTabAre(2)
        .checkStaffDeployedOnDesksAndQueuesTabAre(2)
        .checkStaffAvailableOnDesksAndQueuesTabAre(2)
        .findAndClick('Staff Movements')
        .checkStaffNumbersOnMovementsTabAre(2)
        .checkUserNameOnMovementsTab(2, "Unknown")
        .removeXMovements(2);
    });
  });
});

Cypress.Commands.add('addMovementFor1HourAt', (numStaff, hour) => cy.addOrRemoveMovementFor1HourAt("add", numStaff, hour));

Cypress.Commands.add('removeMovementFor1HourAt', (numStaff, hour) => cy.addOrRemoveMovementFor1HourAt("remove", numStaff, hour));

Cypress.Commands.add('staffDeployedAtRow', (row) => {
  const selector = '#sticky-body > :nth-child(' + (row + 1) + ') > :nth-child(14)';
  cy.get(selector);
});

Cypress.Commands.add('staffMovementsAtRow', (row) => {
  const selector = 'td.non-pcp:nth($index)';
  cy.get(selector.replace('$index', row * 2 + 1));
});

Cypress.Commands.add('staffAvailableAtRow', (row) => {
  const selector = ':nth-child($index) > .staff-adjustments > :nth-child(1) > .deployed';
  cy.get(selector.replace('$index', row + 1));
});

Cypress.Commands.add('staffOverTheDayAtSlot', (slot) => {
  const selector = '#available-staff tbody > :nth-child(2) > :nth-child(' + (slot + 1) + ')';
  cy.get(selector);
});

Cypress.Commands.add('checkStaffAvailableOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1].map((row) => {
    cy.staffAvailableAtRow(row).contains(numStaff);
  });
  cy.staffAvailableAtRow(4, 0).contains(0);
});

Cypress.Commands.add('checkStaffMovementsOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1].map((row) => {
    cy.staffMovementsAtRow(row).contains(numStaff);
  });
  cy.staffMovementsAtRow(4).contains(0);
});

Cypress.Commands.add('checkStaffDeployedOnDesksAndQueuesTabAre', (numStaff) => {
  [0, 1].map((row) => {
    cy.staffDeployedAtRow(row).contains(numStaff);
  });
  cy.staffDeployedAtRow(4).contains(0);
});

Cypress.Commands.add('checkStaffNumbersOnMovementsTabAre', (numStaff) => {
  cy.contains("Staff Movements").click().then(() => {
    [0, 1].map((slot) => { cy.staffOverTheDayAtSlot(slot).contains(numStaff) });
    cy.staffOverTheDayAtSlot(4).contains("0");
  });
});

Cypress.Commands.add('removeXMovements', (numToRemove) => {
  cy.contains("Staff Movements").click().then(() => {
    for (let i = 1; i <= numToRemove; i++) {
      cy.get('.fa-remove').first().click().end();
      cy.get('.fa-remove').should('have.length', numToRemove - i).end();
    }
  });
});

Cypress.Commands.add('checkUserNameOnMovementsTab', (numMovements, userName) => {
  cy.get('.movement-display')
    .should('have.length', numMovements)
    .each(($element, index, $lis) => {
      cy.wrap($element).contains(userName);
      cy.wrap($element);
    });
});

Cypress.Commands.add('addOrRemoveMovementFor1HourAt', (addOrRemove, numStaff, hour) => {
  const buttonLabel = addOrRemove == "add" ? "+" : "-";
  const buttonNth = addOrRemove == "add" ? 3 : 1;

  cy.get('#sticky-body tr:nth-child(1) td').should('have.length', 15).then(() => {
    for (let i = 0; i < numStaff; i++) {
      cy.get('.staff-adjustments > :nth-child(' + (hour + 1) + ') > :nth-child(' + buttonNth + ')').contains(buttonLabel).then((el) => {
        el.click();
        cy.get('#staff-adjustment-dialogue').contains("Save").then((saveButton) => {
          saveButton.click();
        })
      });
    }
  });
});
<|endoftext|>


<commit_before>/* eslint-disable max-len */
// These get set by the ResizeComponent
export const GUI = {
  innerWidth: null,
  innerHeight: null,
  columnWidth: null,
  contentWidth: null,
  coverOffset: null,
  coverImageSize: null,
  viewportDeviceSize: null,
  gridColumnCount: null,
}

export const SHORTCUT_KEYS = {
  DISCOVER: 'd',
  DT_CONTAINER_TOGGLE: 'g |',
  DT_GRID_CYCLE: 'g ~',
  DT_GRID_TOGGLE: 'g `',
  ESC: 'esc',
  FOLLOWING: 'f',
  FULLSCREEN: 'g m',
  HELP: '?',
  NOTIFICATIONS: 'r',
  OMNIBAR: '2',
  SEARCH: 's',
  STARRED: 'n',
  TOGGLE_LAYOUT: '=',
}

export const FORM_CONTROL_STATUS = {
  INDETERMINATE: 'FORM_CONTROL_STATUS.INDETERMINATE',
  REQUEST: 'FORM_CONTROL_STATUS.REQUEST',
  FAILURE: 'FORM_CONTROL_STATUS.FAILURE',
  SUCCESS: 'FORM_CONTROL_STATUS.SUCCESS',
  SUBMITTED: 'FORM_CONTROL_STATUS.SUBMITTED',
}

export const SETTINGS = {
  NSFW_DISCLAIMER: 'Note: Apple iOS rules block the sharing of NSFW content through iOS apps. Accounts & posts marked NSFW will not appear in search results on the Ello iOS App but will appear when using Ello on the web. If you want your posts to appear on Ello iOS, please set "Post Adult Content" to "No".',
  YOUR_DATA_DESC: 'The only data that Ello stores about you is what you enter on your settings page and what you post. All other usage data that we collect is anonymized, and you can opt out.',
  ACCOUNT_DELETION_DEFINITION: {
    term: 'Delete Account',
    desc: 'By deleting your account you remove your personal information from Ello. Your account cannot be restored.',
  },
}

export const PREFERENCES = {
  NSFW_VIEW: {
    term: 'View Adult Content',
    desc: 'What does this mean?',
  },
  NSFW_POST: {
    term: 'Post Adult Content',
    desc: 'What does this mean?',
  },
}

export const SESSION_KEYS = {
  NOTIFICATIONS_FILTER: 'KEYS.NOTIFICATIONS_FILTER',
  scrollLocationKey: (key) => `scrollLocations.${key}`,
}

export const ERROR_MESSAGES = {
  NONE: '',
  EMAIL: {
    INVALID: 'That email is invalid. Please try again.',
  },
  INVITATION_CODE: {
    INVALID: 'That code is invalid. Please try again.',
  },
  PASSWORD: {
    TOO_SHORT: 'must be at least 8 characters.',
  },
  USERNAME: {
    EXISTS: 'Username already exists. Please try a new one. You can change your username at any time',
    INVALID_CHARACTERS: 'Username must only contain letters, numbers, underscores & dashes.',
    INVALID: 'That username is invalid. Please try again.',
    SPACES: 'Username must not contain a space.',
  },
}

export const ONBOARDING_VERSION = '1.1'

<commit_msg>Set onboarding version to production 1<commit_after>/* eslint-disable max-len */
// These get set by the ResizeComponent
export const GUI = {
  innerWidth: null,
  innerHeight: null,
  columnWidth: null,
  contentWidth: null,
  coverOffset: null,
  coverImageSize: null,
  viewportDeviceSize: null,
  gridColumnCount: null,
}

export const SHORTCUT_KEYS = {
  DISCOVER: 'd',
  DT_CONTAINER_TOGGLE: 'g |',
  DT_GRID_CYCLE: 'g ~',
  DT_GRID_TOGGLE: 'g `',
  ESC: 'esc',
  FOLLOWING: 'f',
  FULLSCREEN: 'g m',
  HELP: '?',
  NOTIFICATIONS: 'r',
  OMNIBAR: '2',
  SEARCH: 's',
  STARRED: 'n',
  TOGGLE_LAYOUT: '=',
}

export const FORM_CONTROL_STATUS = {
  INDETERMINATE: 'FORM_CONTROL_STATUS.INDETERMINATE',
  REQUEST: 'FORM_CONTROL_STATUS.REQUEST',
  FAILURE: 'FORM_CONTROL_STATUS.FAILURE',
  SUCCESS: 'FORM_CONTROL_STATUS.SUCCESS',
  SUBMITTED: 'FORM_CONTROL_STATUS.SUBMITTED',
}

export const SETTINGS = {
  NSFW_DISCLAIMER: 'Note: Apple iOS rules block the sharing of NSFW content through iOS apps. Accounts & posts marked NSFW will not appear in search results on the Ello iOS App but will appear when using Ello on the web. If you want your posts to appear on Ello iOS, please set "Post Adult Content" to "No".',
  YOUR_DATA_DESC: 'The only data that Ello stores about you is what you enter on your settings page and what you post. All other usage data that we collect is anonymized, and you can opt out.',
  ACCOUNT_DELETION_DEFINITION: {
    term: 'Delete Account',
    desc: 'By deleting your account you remove your personal information from Ello. Your account cannot be restored.',
  },
}

export const PREFERENCES = {
  NSFW_VIEW: {
    term: 'View Adult Content',
    desc: 'What does this mean?',
  },
  NSFW_POST: {
    term: 'Post Adult Content',
    desc: 'What does this mean?',
  },
}

export const SESSION_KEYS = {
  NOTIFICATIONS_FILTER: 'KEYS.NOTIFICATIONS_FILTER',
  scrollLocationKey: (key) => `scrollLocations.${key}`,
}

export const ERROR_MESSAGES = {
  NONE: '',
  EMAIL: {
    INVALID: 'That email is invalid. Please try again.',
  },
  INVITATION_CODE: {
    INVALID: 'That code is invalid. Please try again.',
  },
  PASSWORD: {
    TOO_SHORT: 'must be at least 8 characters.',
  },
  USERNAME: {
    EXISTS: 'Username already exists. Please try a new one. You can change your username at any time',
    INVALID_CHARACTERS: 'Username must only contain letters, numbers, underscores & dashes.',
    INVALID: 'That username is invalid. Please try again.',
    SPACES: 'Username must not contain a space.',
  },
}

export const ONBOARDING_VERSION = '1'

<|endoftext|>


<commit_before><commit_msg>task model completed<commit_after><|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import DocumentTitle from 'react-document-title';
import FontAwesome from 'react-fontawesome';
import { Button, ControlLabel, DropdownButton, Modal, FormControl, FormGroup, MenuItem } from 'react-bootstrap';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import styled, {
  css
} from 'styled-components';
import { AppApi } from 'lattice';

import CreateApp from './CreateApp';
import CreateAppType from './CreateAppType';
import Page from '../../components/page/Page';
import { fetchOrganizationsRequest } from '../organizations/actions/OrganizationsActionFactory';
import * as actionFactory from './AppActionFactory';
import styles from './app.module.css';

const AppSectionContainer = styled.div`
  display: flex;
  flex-direction: row;
  margin: 10px 0;
`;

const ButtonContainer = styled.div`
  display: inline;
`;

const AppContainer = styled.div`
  display: inline;
  margin-left: 10px;
`;

const AppSubSectionContainer = styled.div`
  margin-left: 10px;
  `;

const AppTitle = styled.div`
  font-size: 16px;
  font-weight: bold;
`;

const InstallHelperText = styled.div`
  font-size: 14px;
  margin-bottom: 7px;
`;

const InstallButtonWrapper = styled.div`
  margin-top: 15px;
  text-align: center;
`;

const OrganizationSelectionWrapper = styled.div`
  margin-bottom: 30px;
`;

const ModalBodyContainer = styled.div`
  text-align: center;
`;

const NoOrganizationsText = styled.div`
  font-size: 18px;
  margin-bottom: 20px;
`;

const ErrorMessageWrapper = styled.div`
  text-align: center;
`;

const ErrorMessage = styled.div`
  font-size: 14px;
  color: red;
`;

class Apps extends React.Component {
  static propTypes = {
    auth: PropTypes.object.isRequired,
    apps: PropTypes.instanceOf(Immutable.List).isRequired,
    appTypes: PropTypes.instanceOf(Immutable.Map).isRequired,
    errorMessage: PropTypes.string.isRequired,
    organizations: PropTypes.instanceOf(Immutable.Map).isRequired,
    getAppsRequest: PropTypes.func.isRequired,
    // getAppTypesForAppTypeIds: PropTypes.func.isRequired,
    deleteAppRequest: PropTypes.func.isRequired,
    getOwnedOrganizations: PropTypes.func.isRequired,
    install: PropTypes.func.isRequired
  }

  constructor(props) {
    super(props);
    this.state = {
      installing: null,
      prefix: '',
      org: '',
      isAppModalOpen: false,
      isAppTypeModalOpen: false,
      addAppTypeAppId: '',
      addAppTypeAppTitle: '',
      addAppTypeAppTypeId: '',
      isAddAppTypeToAppModalOpen: false
    };
  }

  componentDidMount() {
    this.props.getAppsRequest();
    this.props.getOwnedOrganizations();
    // this.props.getAppTypesForAppTypeIds();
  }

  componentWillReceiveProps(nextProps) {
    if (!this.props.organizations.size && nextProps.organizations.size) {
      this.setState({ org: nextProps.organizations.keySeq().get(0) });
    }
  }

  onAddApp = () => {
    this.setState({
      isAppModalOpen: true
    });
  };

  onDeleteApp = (app) => {
    this.props.deleteAppRequest(app.get('id'));
  };

  onDeleteAppTypeFromApp = (appId, appTypeId) => {
    AppApi.removeAppTypeFromApp(appId, appTypeId);
  }

  onAddAppTypeToApp = (appId) => {
    console.log(appId);
    this.setState({
      isAddAppTypeToAppModalOpen: true
    });
  }

  renderAddAppForm = () => {
    return (
      <form onSubmit={() => {
        AppApi.addAppTypeToApp(this.state.addAppTypeAppId, this.state.addAppTypeAppTypeId);
        this.closeModal();
      }}>
        <FormGroup>
          <ControlLabel>Enter an App Type Id</ControlLabel>
          <FormControl type="text" onChange={(e) => {
            this.setState({ addAppTypeAppTypeId: e.target.value });
          }} />
        </FormGroup>

        <Button type="submit" bsStyle="primary">Submit</Button>
      </form>
    );
  }

  onAddAppType = () => {
    this.setState({
      isAppTypeModalOpen: true
    });
  };

  closeModal = () => {
    this.setState({
      isAppModalOpen: false,
      isAppTypeModalOpen: false,
      isAddAppTypeToAppModalOpen: false
    });
  };

  getAppTypeIdByName = () => {
    // REFERENCE ONLY
    // for printing out an id for reference
    const appTypeIdFromApi = AppApi.getAppTypeByFqn('sixth', 'apptype');
    console.log(appTypeIdFromApi);
    return appTypeIdFromApi;
  }

  collectAppTypesFromApp = (app) => {
    // REMOVE THE FOLLOWING LINE WHEN DONE!
    // this.getAppTypeIdByName();
    const appTypeIdsFromApp = app.get('appTypeIds');
    const appTypesFromApp = [];

    for (let i = 0; i < appTypeIdsFromApp.size; i += 1) {
      appTypesFromApp.push(appTypeIdsFromApp.get(i));
    }
    return appTypesFromApp;
  }

  renderAppType = (app) => {
    // get the appTypeIds for the app
    // for each appTypeId lookup the appTypes
    const appTypeList = this.collectAppTypesFromApp(app);
    const appTypes = this.props.appTypes;
    const appNames = [];

    if (appTypes.get(appTypeList[0])) {
      for (let i = 0; i < appTypeList.length; i += 1) {
        const eachApp = appTypes.get(appTypeList[i]);
        appNames.push(
          <AppSectionContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  bsSize="xsmall"
                  onClick={() => {
                    this.onDeleteAppTypeFromApp(app.get('id'), eachApp.get('id'));
                  }}>
                <FontAwesome name="minus" />
              </Button>
            </ButtonContainer>
            <AppSubSectionContainer>

 {eachApp.get('title')}, {eachApp.get('id')}

            </AppSubSectionContainer>
          </AppSectionContainer>);
      }
    }
    return (
      appNames
    );
  }

  renderApps = () => {
    return this.props.apps.map((app) => {

      const { isAddAppTypeToAppModalOpen } = this.state;

      return (

          <AppSectionContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  onClick={() => {
                    this.setState({ installing: app });
                  }}>
                <FontAwesome name="plus" />
              </Button>
            </ButtonContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  onClick={() => {
                    this.onDeleteApp(app);
                  }}>
                <FontAwesome name="minus" />
              </Button>
            </ButtonContainer>
            <AppContainer>
              <AppTitle>{app.get('title')}</AppTitle>

{app.get('description')}

Id: {app.get('id')}

              <AppSectionContainer>
                <ButtonContainer>
                  <Button
                      bsStyle="default"
                      bsSize="xsmall"
                      onClick={() => {
                        this.onAddAppTypeToApp(app.get('id'));
                        this.setState({
                          addAppTypeAppId: app.get('id'),
                          addAppTypeAppTitle: app.get('title')
                        });
                      }}>
                    <FontAwesome name="plus" />
                  </Button>
                </ButtonContainer>
                <AppSubSectionContainer>

App Types:

                </AppSubSectionContainer>
              </AppSectionContainer>
              <Modal show={isAddAppTypeToAppModalOpen} onHide={this.closeModal} container={this}>
                <Modal.Header closeButton>
                  <Modal.Title>Add an App Type to {this.state.addAppTypeAppTitle} app</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                  {this.renderAddAppForm()}
                </Modal.Body>
              </Modal>
              {this.renderAppType(app)}
            </AppContainer>
          </AppSectionContainer>

      );
    });
  }

  renderOrganizationSection = () => {
    const orgOptions = this.props.organizations.map((organization) => {
      const id = organization.get('id');
      const title = organization.get('title');
      return (
        <MenuItem
            key={id}
            eventKey={id}
            onClick={() => {
              this.setState({ org: id });
            }}>
          {title}
        </MenuItem>
      );
    });
    const selectedOrg = this.props.organizations.get(this.state.org);
    const selectedOrgTitle = selectedOrg.get('title');
    return (
      <OrganizationSelectionWrapper>
        <InstallHelperText>Select which organization you would like to download this app for.</InstallHelperText>
        <DropdownButton title={selectedOrgTitle} id="organization_select">
          {orgOptions}
        </DropdownButton>
      </OrganizationSelectionWrapper>
    );
  }

  renderPrefixSection = () => {
    return (
      <FormGroup>
        <ControlLabel>Choose a unique prefix for the datasets that will be used for this app.</ControlLabel>
        <FormControl
          type="text"
          value={this.state.prefix}
          onChange={(e) => {
            this.setState({ prefix: e.target.value });
          }} />
      </FormGroup>
    );
  }

  closeInstallModal = () => {
    this.setState({
      installing: null,
      prefix: ''
    });
  }

  install = () => {
    const appId = this.state.installing.get('id');
    const organizationId = this.state.org;
    const prefix = this.state.prefix;
    this.props.install(appId, organizationId, prefix);
    this.closeInstallModal();
  }

  renderError = () => {
    const error = this.props.errorMessage;
    if (!error || !error.length) return null;
    return (
      <ErrorMessageWrapper>
        <ErrorMessage>{error}</ErrorMessage>
      </ErrorMessageWrapper>
    );
  }

  renderModalBody = () => {
    if (!this.props.organizations.size) {
      return (
        <ModalBodyContainer>
          <NoOrganizationsText>You must be an owner of an organization to install an app.</NoOrganizationsText>
          <Link to='orgs/new'>Create an organization</Link>
        </ModalBodyContainer>
      );
    }
    return (
      <ModalBodyContainer>
        {this.renderOrganizationSection()}
        {this.renderPrefixSection()}
        <InstallButtonWrapper>
          <Button bsStyle="primary" onClick={this.install}>Install</Button>
        </InstallButtonWrapper>
      </ModalBodyContainer>
    );
  }

  renderInstallModal = () => {
    const app = this.state.installing;
    if (!app) return null;
    return (
      <Modal
          show={!!app}
          onHide={this.closeInstallModal}>
        <Modal.Header closeButton>
          <Modal.Title>Install {app.get('title')}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          {this.renderModalBody()}
        </Modal.Body>
      </Modal>
    );
  }

  render() {
    const { isAppModalOpen } = this.state;
    const { isAppTypeModalOpen } = this.state;

    return (
      <DocumentTitle title="Apps">
        <Page>
          <Page.Header>
            <Page.Title>Browse Apps</Page.Title>
            <Button bsStyle="primary" className={styles.control} onClick={this.onAddApp}>
              <FontAwesome name="plus-circle" size="lg" /> App
            </Button>
            <Button bsStyle="primary" className={styles.control} onClick={this.onAddAppType}>
              <FontAwesome name="plus-circle" size="lg" /> App Type
            </Button>
          </Page.Header>
          <Modal show={isAppModalOpen} onHide={this.closeModal} container={this}>
            <Modal.Header closeButton>
              <Modal.Title>Create an App</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <CreateApp />
            </Modal.Body>
          </Modal>
          <Modal show={isAppTypeModalOpen} onHide={this.closeModal} container={this}>
            <Modal.Header closeButton>
              <Modal.Title>Create an App Type</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <CreateAppType />
            </Modal.Body>
          </Modal>
          <Page.Body>
            {this.renderError()}
            {this.renderApps()}
            {this.renderInstallModal()}
          </Page.Body>
        </Page>
      </DocumentTitle>
    );
  }
}

function mapStateToProps(state, ownProps) {
  const apps = state.getIn(['app', 'apps'], Immutable.List());
  const errorMessage = state.getIn(['app', 'errorMessage'], '');
  const appTypes = state.getIn(['app', 'appTypes'], Immutable.Map());
  const organizations = state.getIn(['organizations', 'organizations'], Immutable.Map())
    .filter((organization) => {
      return organization.get('isOwner');
    });

  return { apps, appTypes, errorMessage, organizations };
}

function mapDispatchToProps(dispatch) {
  const actions = {
    getAppsRequest: () => {
      dispatch(actionFactory.getApps());
    },
    getAppTypesForAppTypeIds: (appTypeIds) => {
      dispatch(actionFactory.getAppTypesForAppTypeIdsRequest(appTypeIds));
    },
    getOwnedOrganizations: () => {
      dispatch(fetchOrganizationsRequest());
    },
    install: (appId, organizationId, prefix) => {
      dispatch(actionFactory.installAppRequest(appId, organizationId, prefix));
    },
    deleteAppRequest: (App) => {
      dispatch(actionFactory.deleteAppRequest(App));
    }
  };
  return actions;
}

export default connect(mapStateToProps, mapDispatchToProps)(Apps);
<commit_msg>Fixed some minor linter errors.<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import DocumentTitle from 'react-document-title';
import FontAwesome from 'react-fontawesome';
import { Button, ControlLabel, DropdownButton, Modal, FormControl, FormGroup, MenuItem } from 'react-bootstrap';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import styled from 'styled-components';
import { AppApi } from 'lattice';

import CreateApp from './CreateApp';
import CreateAppType from './CreateAppType';
import Page from '../../components/page/Page';
import { fetchOrganizationsRequest } from '../organizations/actions/OrganizationsActionFactory';
import * as actionFactory from './AppActionFactory';
import styles from './app.module.css';

const AppSectionContainer = styled.div`
  display: flex;
  flex-direction: row;
  margin: 10px 0;
`;

const ButtonContainer = styled.div`
  display: inline;
`;

const AppContainer = styled.div`
  display: inline;
  margin-left: 10px;
`;

const AppSubSectionContainer = styled.div`
  margin-left: 10px;
  `;

const AppTitle = styled.div`
  font-size: 16px;
  font-weight: bold;
`;

const InstallHelperText = styled.div`
  font-size: 14px;
  margin-bottom: 7px;
`;

const InstallButtonWrapper = styled.div`
  margin-top: 15px;
  text-align: center;
`;

const OrganizationSelectionWrapper = styled.div`
  margin-bottom: 30px;
`;

const ModalBodyContainer = styled.div`
  text-align: center;
`;

const NoOrganizationsText = styled.div`
  font-size: 18px;
  margin-bottom: 20px;
`;

const ErrorMessageWrapper = styled.div`
  text-align: center;
`;

const ErrorMessage = styled.div`
  font-size: 14px;
  color: red;
`;

class Apps extends React.Component {
  static propTypes = {
    // auth: PropTypes.object.isRequired,
    apps: PropTypes.instanceOf(Immutable.List).isRequired,
    appTypes: PropTypes.instanceOf(Immutable.Map).isRequired,
    errorMessage: PropTypes.string.isRequired,
    organizations: PropTypes.instanceOf(Immutable.Map).isRequired,
    getAppsRequest: PropTypes.func.isRequired,
    // getAppTypesForAppTypeIds: PropTypes.func.isRequired,
    deleteAppRequest: PropTypes.func.isRequired,
    getOwnedOrganizations: PropTypes.func.isRequired,
    install: PropTypes.func.isRequired
  }

  constructor(props) {
    super(props);
    this.state = {
      installing: null,
      prefix: '',
      org: '',
      isAppModalOpen: false,
      isAppTypeModalOpen: false,
      addAppTypeAppId: '',
      addAppTypeAppTitle: '',
      addAppTypeAppTypeId: '',
      isAddAppTypeToAppModalOpen: false
    };
  }

  componentDidMount() {
    this.props.getAppsRequest();
    this.props.getOwnedOrganizations();
    // this.props.getAppTypesForAppTypeIds();
  }

  componentWillReceiveProps(nextProps) {
    if (!this.props.organizations.size && nextProps.organizations.size) {
      this.setState({ org: nextProps.organizations.keySeq().get(0) });
    }
  }

  onAddApp = () => {
    this.setState({
      isAppModalOpen: true
    });
  };

  onDeleteApp = (app) => {
    this.props.deleteAppRequest(app.get('id'));
  };

  onDeleteAppTypeFromApp = (appId, appTypeId) => {
    AppApi.removeAppTypeFromApp(appId, appTypeId);
  }

  onAddAppTypeToApp = () => {
    this.setState({
      isAddAppTypeToAppModalOpen: true
    });
  }

  renderAddAppForm = () => {
    return (
      <form onSubmit={() => {
        AppApi.addAppTypeToApp(this.state.addAppTypeAppId, this.state.addAppTypeAppTypeId);
        this.closeModal();
      }}>
        <FormGroup>
          <ControlLabel>Enter an App Type Id</ControlLabel>
          <FormControl type="text" onChange={(e) => {
            this.setState({ addAppTypeAppTypeId: e.target.value });
          }} />
        </FormGroup>

        <Button type="submit" bsStyle="primary">Submit</Button>
      </form>
    );
  }

  onAddAppType = () => {
    this.setState({
      isAppTypeModalOpen: true
    });
  };

  closeModal = () => {
    this.setState({
      isAppModalOpen: false,
      isAppTypeModalOpen: false,
      isAddAppTypeToAppModalOpen: false
    });
  };

  getAppTypeIdByName = () => {
    // REFERENCE ONLY
    // for printing out an id for reference
    const appTypeIdFromApi = AppApi.getAppTypeByFqn('sixth', 'apptype');
    console.log(appTypeIdFromApi);
    return appTypeIdFromApi;
  }

  collectAppTypesFromApp = (app) => {
    // REMOVE THE FOLLOWING LINE WHEN DONE!
    // this.getAppTypeIdByName();
    const appTypeIdsFromApp = app.get('appTypeIds');
    const appTypesFromApp = [];

    for (let i = 0; i < appTypeIdsFromApp.size; i += 1) {
      appTypesFromApp.push(appTypeIdsFromApp.get(i));
    }
    return appTypesFromApp;
  }

  renderAppType = (app) => {
    // get the appTypeIds for the app
    // for each appTypeId lookup the appTypes
    const appTypeList = this.collectAppTypesFromApp(app);
    const appTypes = this.props.appTypes;
    const appNames = [];

    if (appTypes.get(appTypeList[0])) {
      for (let i = 0; i < appTypeList.length; i += 1) {
        const eachApp = appTypes.get(appTypeList[i]);
        appNames.push(
          <AppSectionContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  bsSize="xsmall"
                  onClick={() => {
                    this.onDeleteAppTypeFromApp(app.get('id'), eachApp.get('id'));
                  }}>
                <FontAwesome name="minus" />
              </Button>
            </ButtonContainer>
            <AppSubSectionContainer>

 {eachApp.get('title')}, {eachApp.get('id')}

            </AppSubSectionContainer>
          </AppSectionContainer>);
      }
    }
    return (
      appNames
    );
  }

  renderApps = () => {
    return this.props.apps.map((app) => {

      const { isAddAppTypeToAppModalOpen } = this.state;

      return (

          <AppSectionContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  onClick={() => {
                    this.setState({ installing: app });
                  }}>
                <FontAwesome name="plus" />
              </Button>
            </ButtonContainer>
            <ButtonContainer>
              <Button
                  bsStyle="default"
                  onClick={() => {
                    this.onDeleteApp(app);
                  }}>
                <FontAwesome name="minus" />
              </Button>
            </ButtonContainer>
            <AppContainer>
              <AppTitle>{app.get('title')}</AppTitle>

{app.get('description')}

Id: {app.get('id')}

              <AppSectionContainer>
                <ButtonContainer>
                  <Button
                      bsStyle="default"
                      bsSize="xsmall"
                      onClick={() => {
                        this.onAddAppTypeToApp();
                        this.setState({
                          addAppTypeAppId: app.get('id'),
                          addAppTypeAppTitle: app.get('title')
                        });
                      }}>
                    <FontAwesome name="plus" />
                  </Button>
                </ButtonContainer>
                <AppSubSectionContainer>

App Types:

                </AppSubSectionContainer>
              </AppSectionContainer>
              <Modal show={isAddAppTypeToAppModalOpen} onHide={this.closeModal} container={this}>
                <Modal.Header closeButton>
                  <Modal.Title>Add an App Type to {this.state.addAppTypeAppTitle} app</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                  {this.renderAddAppForm()}
                </Modal.Body>
              </Modal>
              {this.renderAppType(app)}
            </AppContainer>
          </AppSectionContainer>

      );
    });
  }

  renderOrganizationSection = () => {
    const orgOptions = this.props.organizations.map((organization) => {
      const id = organization.get('id');
      const title = organization.get('title');
      return (
        <MenuItem
            key={id}
            eventKey={id}
            onClick={() => {
              this.setState({ org: id });
            }}>
          {title}
        </MenuItem>
      );
    });
    const selectedOrg = this.props.organizations.get(this.state.org);
    const selectedOrgTitle = selectedOrg.get('title');
    return (
      <OrganizationSelectionWrapper>
        <InstallHelperText>Select which organization you would like to download this app for.</InstallHelperText>
        <DropdownButton title={selectedOrgTitle} id="organization_select">
          {orgOptions}
        </DropdownButton>
      </OrganizationSelectionWrapper>
    );
  }

  renderPrefixSection = () => {
    return (
      <FormGroup>
        <ControlLabel>Choose a unique prefix for the datasets that will be used for this app.</ControlLabel>
        <FormControl
            type="text"
            value={this.state.prefix}
            onChange={(e) => {
              this.setState({ prefix: e.target.value });
            }} />
      </FormGroup>
    );
  }

  closeInstallModal = () => {
    this.setState({
      installing: null,
      prefix: ''
    });
  }

  install = () => {
    const appId = this.state.installing.get('id');
    const organizationId = this.state.org;
    const prefix = this.state.prefix;
    this.props.install(appId, organizationId, prefix);
    this.closeInstallModal();
  }

  renderError = () => {
    const error = this.props.errorMessage;
    if (!error || !error.length) return null;
    return (
      <ErrorMessageWrapper>
        <ErrorMessage>{error}</ErrorMessage>
      </ErrorMessageWrapper>
    );
  }

  renderModalBody = () => {
    if (!this.props.organizations.size) {
      return (
        <ModalBodyContainer>
          <NoOrganizationsText>You must be an owner of an organization to install an app.</NoOrganizationsText>
          <Link to="orgs/new">Create an organization</Link>
        </ModalBodyContainer>
      );
    }
    return (
      <ModalBodyContainer>
        {this.renderOrganizationSection()}
        {this.renderPrefixSection()}
        <InstallButtonWrapper>
          <Button bsStyle="primary" onClick={this.install}>Install</Button>
        </InstallButtonWrapper>
      </ModalBodyContainer>
    );
  }

  renderInstallModal = () => {
    const app = this.state.installing;
    if (!app) return null;
    return (
      <Modal
          show={!!app}
          onHide={this.closeInstallModal}>
        <Modal.Header closeButton>
          <Modal.Title>Install {app.get('title')}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          {this.renderModalBody()}
        </Modal.Body>
      </Modal>
    );
  }

  render() {
    const { isAppModalOpen } = this.state;
    const { isAppTypeModalOpen } = this.state;

    return (
      <DocumentTitle title="Apps">
        <Page>
          <Page.Header>
            <Page.Title>Browse Apps</Page.Title>
            <Button bsStyle="primary" className={styles.control} onClick={this.onAddApp}>
              <FontAwesome name="plus-circle" size="lg" /> App
            </Button>
            <Button bsStyle="primary" className={styles.control} onClick={this.onAddAppType}>
              <FontAwesome name="plus-circle" size="lg" /> App Type
            </Button>
          </Page.Header>
          <Modal show={isAppModalOpen} onHide={this.closeModal} container={this}>
            <Modal.Header closeButton>
              <Modal.Title>Create an App</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <CreateApp />
            </Modal.Body>
          </Modal>
          <Modal show={isAppTypeModalOpen} onHide={this.closeModal} container={this}>
            <Modal.Header closeButton>
              <Modal.Title>Create an App Type</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <CreateAppType />
            </Modal.Body>
          </Modal>
          <Page.Body>
            {this.renderError()}
            {this.renderApps()}
            {this.renderInstallModal()}
          </Page.Body>
        </Page>
      </DocumentTitle>
    );
  }
}

function mapStateToProps(state) {
  const apps = state.getIn(['app', 'apps'], Immutable.List());
  const errorMessage = state.getIn(['app', 'errorMessage'], '');
  const appTypes = state.getIn(['app', 'appTypes'], Immutable.Map());
  const organizations = state.getIn(['organizations', 'organizations'], Immutable.Map())
    .filter((organization) => {
      return organization.get('isOwner');
    });

  return { apps, appTypes, errorMessage, organizations };
}

function mapDispatchToProps(dispatch) {
  const actions = {
    getAppsRequest: () => {
      dispatch(actionFactory.getApps());
    },
    getAppTypesForAppTypeIds: (appTypeIds) => {
      dispatch(actionFactory.getAppTypesForAppTypeIdsRequest(appTypeIds));
    },
    getOwnedOrganizations: () => {
      dispatch(fetchOrganizationsRequest());
    },
    install: (appId, organizationId, prefix) => {
      dispatch(actionFactory.installAppRequest(appId, organizationId, prefix));
    },
    deleteAppRequest: (App) => {
      dispatch(actionFactory.deleteAppRequest(App));
    }
  };
  return actions;
}

export default connect(mapStateToProps, mapDispatchToProps)(Apps);
<|endoftext|>


<commit_before>/* global describe it */
'use strict'

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const requireInject = require('require-inject')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
require('sinon-as-promised')

chai.use(chaiAsPromised)
chai.use(sinonChai)

const expect = chai.expect

describe('travis.parse', function () {
  it('rejects if file is not found', function () {
    const stubs = {
      path: {
        join: sinon.stub().returns('<travis-file>')
      }
    }
    const logger = {
      error: sinon.spy()
    }
    stubs[require.resolve('../lib/logging')] = {logger}
    stubs[require.resolve('../lib/util')] = {
      exists: sinon.stub().rejects()
    }
    const parse = requireInject('../lib/travis', stubs)
    return expect(parse()).to.be.eventually.rejected.then(function () {
      expect(logger.error).to.have.been.calledWith(
        'Travis file not found: %s', '<travis-file>')
    })
  })
})
<commit_msg>Add language field not found in yaml file test case<commit_after>/* global describe it */
'use strict'

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const requireInject = require('require-inject')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
require('sinon-as-promised')

chai.use(chaiAsPromised)
chai.use(sinonChai)

const expect = chai.expect

describe('travis.parse', function () {
  it('rejects if file is not found', function () {
    const stubs = {
      path: {
        join: sinon.stub().returns('<travis-file>')
      }
    }
    const logger = {
      error: sinon.spy()
    }
    stubs[require.resolve('../lib/logging')] = {logger}
    stubs[require.resolve('../lib/util')] = {
      exists: sinon.stub().rejects()
    }
    const parse = requireInject('../lib/travis', stubs)
    return expect(parse()).to.be.eventually.rejected.then(function () {
      expect(logger.error).to.have.been.calledWith(
        'Travis file not found: %s', '<travis-file>')
    })
  })

  it('rejects if language field is not found', function () {
    const stubs = {
      path: {
        join: sinon.stub().returns('<travis-file>')
      }
    }
    const logger = {
      error: sinon.spy()
    }
    stubs[require.resolve('../lib/logging')] = {logger}
    stubs[require.resolve('../lib/util')] = {
      exists: sinon.stub().resolves(),
      readFile: sinon.stub().resolves('value: 42')
    }
    const parse = requireInject('../lib/travis', stubs)
    return expect(parse()).to.be.eventually.rejected.then(function () {
      expect(logger.error).to.have.been.calledWith(
        'language field not found in travis configuration')
    })
  })
})
<|endoftext|>


<commit_before>'use strict';

var chai = require('chai')
  , expect = chai.expect
  , Support = require(__dirname + '/support')
  , DataTypes = require(__dirname + '/../../lib/data-types')
  , dialect = Support.getTestDialect()
  , _ = require('lodash');

chai.config.includeStack = true;

describe(Support.getTestDialectTeaser('QueryInterface'), function() {
  beforeEach(function(done) {
    this.sequelize.options.quoteIdenifiers = true;
    this.queryInterface = this.sequelize.getQueryInterface();
    done();
  });

  describe('dropAllTables', function() {
    it('should drop all tables', function(done) {
      var self = this;
      this.queryInterface.dropAllTables().complete(function(err) {
        expect(err).to.be.null;

        self.queryInterface.showAllTables().complete(function(err, tableNames) {
          expect(err).to.be.null;
          expect(tableNames).to.be.empty;

          self.queryInterface.createTable('table', { name: DataTypes.STRING }).complete(function(err) {
            expect(err).to.be.null;

            self.queryInterface.showAllTables().complete(function(err, tableNames) {
              expect(err).to.be.null;
              expect(tableNames).to.have.length(1);

              self.queryInterface.dropAllTables().complete(function(err) {
                expect(err).to.be.null;

                self.queryInterface.showAllTables().complete(function(err, tableNames) {
                  expect(err).to.be.null;
                  expect(tableNames).to.be.empty;
                  done();
                });
              });
            });
          });
        });
      });
    });

    it('should be able to skip given tables', function(done) {
      var self = this;
      self.queryInterface.createTable('skipme', {
        name: DataTypes.STRING
      }).success(function() {
        self.queryInterface.dropAllTables({skip: ['skipme']}).complete(function(err) {
          expect(err).to.be.null;

          self.queryInterface.showAllTables().complete(function(err, tableNames) {
            expect(err).to.be.null;

            if (dialect === 'mssql' /* current.dialect.supports.schemas */) {
              tableNames = _.pluck(tableNames, 'tableName');
            }

            expect(tableNames).to.contain('skipme');
            done();
          });
        });
      });
    });
  });

  describe('indexes', function() {
    beforeEach(function(done) {
      var self = this;
      this.queryInterface.dropTable('Group').success(function() {
        self.queryInterface.createTable('Group', {
          username: DataTypes.STRING,
          isAdmin: DataTypes.BOOLEAN,
          from: DataTypes.STRING
        }).success(function() {
          done();
        });
      });
    });

    it('adds, reads and removes an index to the table', function(done) {
      var self = this;

      this.queryInterface.addIndex('Group', ['username', 'isAdmin']).complete(function(err) {
        expect(err).to.be.null;

        self.queryInterface.showIndex('Group').complete(function(err, indexes) {
          expect(err).to.be.null;

          var indexColumns = _.uniq(indexes.map(function(index) { return index.name; }));

          expect(indexColumns).to.include('group_username_is_admin');

          self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).complete(function(err) {
            expect(err).to.be.null;

            self.queryInterface.showIndex('Group').complete(function(err, indexes) {
              expect(err).to.be.null;
              indexColumns = _.uniq(indexes.map(function(index) { return index.name; }));
              expect(indexColumns).to.be.empty;

              done();
            });
          });
        });
      });
    });

    it('does not fail on reserved keywords', function() {
      return this.queryInterface.addIndex('Group', ['from']);
    });
  });

  describe('describeTable', function() {
    it('reads the metadata of the table', function(done) {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: DataTypes.STRING,
        isAdmin: DataTypes.BOOLEAN,
        enumVals: DataTypes.ENUM('hello', 'world')
      }, { freezeTableName: true });

      Users.sync({ force: true }).success(function() {
        self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
          expect(err).to.be.null;
          var username = metadata.username;
          var isAdmin = metadata.isAdmin;
          var enumVals = metadata.enumVals;

          var assertVal = 'VARCHAR(255)';
          switch (dialect) {
            case 'postgres':
              assertVal = 'CHARACTER VARYING';
              break;
            case 'mssql':
              assertVal = 'NVARCHAR';
              break;
          }
          expect(username.type).to.equal(assertVal);
          expect(username.allowNull).to.be.true;
          expect(username.defaultValue).to.be.null;

          assertVal = 'TINYINT(1)';
          switch (dialect) {
            case 'postgres':
              assertVal = 'BOOLEAN';
              break;
            case 'mssql':
              assertVal = 'BIT';
              break;
          }
          expect(isAdmin.type).to.equal(assertVal);
          expect(isAdmin.allowNull).to.be.true;
          expect(isAdmin.defaultValue).to.be.null;

          if (dialect === 'postgres' || dialect === 'postgres-native') {
            expect(enumVals.special).to.be.instanceof(Array);
            expect(enumVals.special).to.have.length(2);
          }
          done();
        });
      });
    });
  });

  describe('createTable', function() {
    it('should create a auto increment primary key', function() {
      return this.queryInterface.createTable('TableWithPK', {
        table_id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.insert(null, 'TableWithPK', {}, {raw: true, returning: true, plain: true}).then(function(response) {
          expect(response.table_id || (typeof response !== 'object' && response)).to.be.ok;
        });
      });
    });

    it('should work with enums (1)', function() {
      return this.queryInterface.createTable('SomeTable', {
        someEnum: DataTypes.ENUM('value1', 'value2', 'value3')
      });
    });

    it('should work with enums (2)', function() {
      return this.queryInterface.createTable('SomeTable', {
        someEnum: {
          type: DataTypes.ENUM,
          values: ['value1', 'value2', 'value3']
        }
      });
    });

    it('should work with schemas', function() {
      var self = this;
      return self.sequelize.dropAllSchemas().then(function() {
        return self.sequelize.createSchema('hero');
      }).then(function() {
        return self.queryInterface.createTable('User', {
          name: {
            type: DataTypes.STRING
          }
        }, {
          schema: 'hero'
        });
      }).then(function() {
        return self.queryInterface.rawSelect('User', {
          schema: 'hero'
        }, 'name');
      });
    });
  });

  describe('renameColumn', function() {
    it('rename a simple column', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: DataTypes.STRING
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'username', 'pseudo');
      });
    });

    it('works with schemas', function() {
      var self = this;
      var Users = self.sequelize.define('User', {
        username: DataTypes.STRING
      }, {
        tableName: 'Users',
        schema: 'archive'
      });

      return self.sequelize.dropAllSchemas().then(function() {
        return self.sequelize.createSchema('archive');
      }).then(function() {
        return Users.sync({ force: true }).then(function() {
          return self.queryInterface.renameColumn({
            schema: 'archive',
            tableName: 'Users'
          }, 'username', 'pseudo');
        });
      });
    });

    it('rename a column non-null without default value', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: {
          type: DataTypes.STRING,
          allowNull: false
        }
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'username', 'pseudo');
      });
    });

    it('rename a boolean column non-null without default value', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        active: {
          type: DataTypes.BOOLEAN,
          allowNull: false,
          defaultValue: false
        }
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'active', 'enabled');
      });
    });

    it('renames a column primary key autoIncrement column', function() {
      var self = this;
      var Fruits = self.sequelize.define('Fruit', {
        fruitId: {
          type: DataTypes.INTEGER,
          allowNull: false,
          primaryKey: true,
          autoIncrement: true
        }
      }, { freezeTableName: true });

      return Fruits.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('Fruit', 'fruitId', 'fruit_id');
      });
    });
  });

  describe('changeColumn', function() {
    it('should support schemas', function() {
      return this.sequelize.dropAllSchemas().bind(this).then(function() {
        return this.sequelize.createSchema('archive');
      }).then(function() {
        return this.queryInterface.createTable({
          tableName: 'users',
          schema: 'archive'
        }, {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          currency: DataTypes.INTEGER
        }).bind(this).then(function() {
          return this.queryInterface.changeColumn({
            tableName: 'users',
            schema: 'archive'
          }, 'currency', {
            type: DataTypes.FLOAT
          });
        });
      });
    });
  });

  describe('addColumn', function() {
    beforeEach(function() {
      return this.queryInterface.createTable('users', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      });
    });

    it('should be able to add a foreign key reference', function() {
      return this.queryInterface.createTable('level', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.addColumn('users', 'level_id', {
          type: DataTypes.INTEGER,
          references: 'level',
          referenceKey: 'id',
          onUpdate: 'cascade',
          onDelete: 'set null'
        });
      });
    });

    it('should work with schemas', function() {
      return this.queryInterface.createTable({
        tableName: 'users',
        schema: 'archive'
      }, {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.addColumn({
          tableName: 'users',
          schema: 'archive'
        }, 'level_id', {
          type: DataTypes.INTEGER
        });
      });
    });

    it('should work with enums (1)', function() {
      return this.queryInterface.addColumn('users', 'someEnum', DataTypes.ENUM('value1', 'value2', 'value3'));
    });

    it('should work with enums (2)', function() {
      return this.queryInterface.addColumn('users', 'someOtherEnum', {
        type: DataTypes.ENUM,
        values: ['value1', 'value2', 'value3']
      });
    });
  });

  describe('describeForeignKeys', function() {
    beforeEach(function() {
      return this.queryInterface.createTable('users', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.createTable('hosts', {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          admin: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id'
          },
          operator: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id',
            onUpdate: 'cascade'
          },
          owner: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id',
            onUpdate: 'cascade',
            onDelete: 'set null'
          }
        });
      });
    });

    it('should get a list of foreign keys for the table', function() {
      var sql = this.queryInterface.QueryGenerator.getForeignKeysQuery('hosts', this.sequelize.config.database);

      return this.sequelize.query(sql, {type: this.sequelize.QueryTypes.FOREIGNKEYS}).then(function(fks) {
        expect(fks).to.have.length(3);
        var keys = Object.keys(fks[0]),
          keys2 = Object.keys(fks[1]),
          keys3 = Object.keys(fks[2]);

        if (dialect === 'postgres' || dialect === 'postgres-native') {
          expect(keys).to.have.length(6);
          expect(keys2).to.have.length(7);
          expect(keys3).to.have.length(7);
        } else if (dialect === 'sqlite') {
          expect(keys).to.have.length(8);
        } else if (dialect === 'mysql' || dialect === 'mssql') {
          expect(keys).to.have.length(1);
        } else {
          console.log("This test doesn't support " + dialect);
        }
      });
    });
  });
});
<commit_msg>Refactor all query interface tests to use promises<commit_after>'use strict';

var chai = require('chai')
  , expect = chai.expect
  , Support = require(__dirname + '/support')
  , DataTypes = require(__dirname + '/../../lib/data-types')
  , dialect = Support.getTestDialect()
  , _ = require('lodash');

chai.config.includeStack = true;

describe(Support.getTestDialectTeaser('QueryInterface'), function() {
  beforeEach(function() {
    this.sequelize.options.quoteIdenifiers = true;
    this.queryInterface = this.sequelize.getQueryInterface();
  });

  describe('dropAllTables', function() {
    it('should drop all tables', function() {
      var self = this;
      return this.queryInterface.dropAllTables().then(function() {
        return self.queryInterface.showAllTables().then(function(tableNames) {
          expect(tableNames).to.be.empty;
          return self.queryInterface.createTable('table', { name: DataTypes.STRING }).then(function() {
            return self.queryInterface.showAllTables().then(function(tableNames) {
              expect(tableNames).to.have.length(1);
              return self.queryInterface.dropAllTables().then(function() {
                return self.queryInterface.showAllTables().then(function(tableNames) {
                  expect(tableNames).to.be.empty;
                });
              });
            });
          });
        });
      });
    });

    it('should be able to skip given tables', function() {
      var self = this;
      return self.queryInterface.createTable('skipme', {
        name: DataTypes.STRING
      }).then(function() {
        return self.queryInterface.dropAllTables({skip: ['skipme']}).then(function() {
          return self.queryInterface.showAllTables().then(function(tableNames) {
            if (dialect === 'mssql' /* current.dialect.supports.schemas */) {
              tableNames = _.pluck(tableNames, 'tableName');
            }
            expect(tableNames).to.contain('skipme');
          });
        });
      });
    });
  });

  describe('indexes', function() {
    beforeEach(function() {
      var self = this;
      return this.queryInterface.dropTable('Group').then(function() {
        return self.queryInterface.createTable('Group', {
          username: DataTypes.STRING,
          isAdmin: DataTypes.BOOLEAN,
          from: DataTypes.STRING
        });
      });
    });

    it('adds, reads and removes an index to the table', function() {
      var self = this;
      return this.queryInterface.addIndex('Group', ['username', 'isAdmin']).then(function() {
        return self.queryInterface.showIndex('Group').then(function(indexes) {
          var indexColumns = _.uniq(indexes.map(function(index) { return index.name; }));
          expect(indexColumns).to.include('group_username_is_admin');
          return self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).then(function() {
            return self.queryInterface.showIndex('Group').then(function(indexes) {
              indexColumns = _.uniq(indexes.map(function(index) { return index.name; }));
              expect(indexColumns).to.be.empty;
            });
          });
        });
      });
    });

    it('does not fail on reserved keywords', function() {
      return this.queryInterface.addIndex('Group', ['from']);
    });
  });

  describe('describeTable', function() {
    it('reads the metadata of the table', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: DataTypes.STRING,
        isAdmin: DataTypes.BOOLEAN,
        enumVals: DataTypes.ENUM('hello', 'world')
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.describeTable('_Users').then(function(metadata) {
          var username = metadata.username;
          var isAdmin = metadata.isAdmin;
          var enumVals = metadata.enumVals;

          var assertVal = 'VARCHAR(255)';
          switch (dialect) {
            case 'postgres':
              assertVal = 'CHARACTER VARYING';
              break;
            case 'mssql':
              assertVal = 'NVARCHAR';
              break;
          }
          expect(username.type).to.equal(assertVal);
          expect(username.allowNull).to.be.true;
          expect(username.defaultValue).to.be.null;

          assertVal = 'TINYINT(1)';
          switch (dialect) {
            case 'postgres':
              assertVal = 'BOOLEAN';
              break;
            case 'mssql':
              assertVal = 'BIT';
              break;
          }
          expect(isAdmin.type).to.equal(assertVal);
          expect(isAdmin.allowNull).to.be.true;
          expect(isAdmin.defaultValue).to.be.null;

          if (dialect === 'postgres' || dialect === 'postgres-native') {
            expect(enumVals.special).to.be.instanceof(Array);
            expect(enumVals.special).to.have.length(2);
          }
        });
      });
    });
  });

  describe('createTable', function() {
    it('should create a auto increment primary key', function() {
      return this.queryInterface.createTable('TableWithPK', {
        table_id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.insert(null, 'TableWithPK', {}, {raw: true, returning: true, plain: true}).then(function(response) {
          expect(response.table_id || (typeof response !== 'object' && response)).to.be.ok;
        });
      });
    });

    it('should work with enums (1)', function() {
      return this.queryInterface.createTable('SomeTable', {
        someEnum: DataTypes.ENUM('value1', 'value2', 'value3')
      });
    });

    it('should work with enums (2)', function() {
      return this.queryInterface.createTable('SomeTable', {
        someEnum: {
          type: DataTypes.ENUM,
          values: ['value1', 'value2', 'value3']
        }
      });
    });

    it('should work with schemas', function() {
      var self = this;
      return self.sequelize.dropAllSchemas().then(function() {
        return self.sequelize.createSchema('hero');
      }).then(function() {
        return self.queryInterface.createTable('User', {
          name: {
            type: DataTypes.STRING
          }
        }, {
          schema: 'hero'
        });
      }).then(function() {
        return self.queryInterface.rawSelect('User', {
          schema: 'hero'
        }, 'name');
      });
    });
  });

  describe('renameColumn', function() {
    it('rename a simple column', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: DataTypes.STRING
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'username', 'pseudo');
      });
    });

    it('works with schemas', function() {
      var self = this;
      var Users = self.sequelize.define('User', {
        username: DataTypes.STRING
      }, {
        tableName: 'Users',
        schema: 'archive'
      });

      return self.sequelize.dropAllSchemas().then(function() {
        return self.sequelize.createSchema('archive');
      }).then(function() {
        return Users.sync({ force: true }).then(function() {
          return self.queryInterface.renameColumn({
            schema: 'archive',
            tableName: 'Users'
          }, 'username', 'pseudo');
        });
      });
    });

    it('rename a column non-null without default value', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        username: {
          type: DataTypes.STRING,
          allowNull: false
        }
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'username', 'pseudo');
      });
    });

    it('rename a boolean column non-null without default value', function() {
      var self = this;
      var Users = self.sequelize.define('_Users', {
        active: {
          type: DataTypes.BOOLEAN,
          allowNull: false,
          defaultValue: false
        }
      }, { freezeTableName: true });

      return Users.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('_Users', 'active', 'enabled');
      });
    });

    it('renames a column primary key autoIncrement column', function() {
      var self = this;
      var Fruits = self.sequelize.define('Fruit', {
        fruitId: {
          type: DataTypes.INTEGER,
          allowNull: false,
          primaryKey: true,
          autoIncrement: true
        }
      }, { freezeTableName: true });

      return Fruits.sync({ force: true }).then(function() {
        return self.queryInterface.renameColumn('Fruit', 'fruitId', 'fruit_id');
      });
    });
  });

  describe('changeColumn', function() {
    it('should support schemas', function() {
      return this.sequelize.dropAllSchemas().bind(this).then(function() {
        return this.sequelize.createSchema('archive');
      }).then(function() {
        return this.queryInterface.createTable({
          tableName: 'users',
          schema: 'archive'
        }, {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          currency: DataTypes.INTEGER
        }).bind(this).then(function() {
          return this.queryInterface.changeColumn({
            tableName: 'users',
            schema: 'archive'
          }, 'currency', {
            type: DataTypes.FLOAT
          });
        });
      });
    });
  });

  describe('addColumn', function() {
    beforeEach(function() {
      return this.queryInterface.createTable('users', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      });
    });

    it('should be able to add a foreign key reference', function() {
      return this.queryInterface.createTable('level', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.addColumn('users', 'level_id', {
          type: DataTypes.INTEGER,
          references: 'level',
          referenceKey: 'id',
          onUpdate: 'cascade',
          onDelete: 'set null'
        });
      });
    });

    it('should work with schemas', function() {
      return this.queryInterface.createTable({
        tableName: 'users',
        schema: 'archive'
      }, {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.addColumn({
          tableName: 'users',
          schema: 'archive'
        }, 'level_id', {
          type: DataTypes.INTEGER
        });
      });
    });

    it('should work with enums (1)', function() {
      return this.queryInterface.addColumn('users', 'someEnum', DataTypes.ENUM('value1', 'value2', 'value3'));
    });

    it('should work with enums (2)', function() {
      return this.queryInterface.addColumn('users', 'someOtherEnum', {
        type: DataTypes.ENUM,
        values: ['value1', 'value2', 'value3']
      });
    });
  });

  describe('describeForeignKeys', function() {
    beforeEach(function() {
      return this.queryInterface.createTable('users', {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        }
      }).bind(this).then(function() {
        return this.queryInterface.createTable('hosts', {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          admin: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id'
          },
          operator: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id',
            onUpdate: 'cascade'
          },
          owner: {
            type: DataTypes.INTEGER,
            references: 'users',
            referenceKey: 'id',
            onUpdate: 'cascade',
            onDelete: 'set null'
          }
        });
      });
    });

    it('should get a list of foreign keys for the table', function() {
      var sql = this.queryInterface.QueryGenerator.getForeignKeysQuery('hosts', this.sequelize.config.database);

      return this.sequelize.query(sql, {type: this.sequelize.QueryTypes.FOREIGNKEYS}).then(function(fks) {
        expect(fks).to.have.length(3);
        var keys = Object.keys(fks[0]),
          keys2 = Object.keys(fks[1]),
          keys3 = Object.keys(fks[2]);

        if (dialect === 'postgres' || dialect === 'postgres-native') {
          expect(keys).to.have.length(6);
          expect(keys2).to.have.length(7);
          expect(keys3).to.have.length(7);
        } else if (dialect === 'sqlite') {
          expect(keys).to.have.length(8);
        } else if (dialect === 'mysql' || dialect === 'mssql') {
          expect(keys).to.have.length(1);
        } else {
          console.log("This test doesn't support " + dialect);
        }
      });
    });
  });
});
<|endoftext|>


<commit_before>M.wrap('github/jillix/navigation/v0.0.1/navigation.js', function (require, module, exports) {

var View = require('github/jillix/view/v0.0.1/view');

// emit dom states
function domStateHandler (self, view, state) {
    return function () {
        // save current active dom elm
        self.active = this;

        // change state
        changeState.call(self, view, state);
    };
}

function changeState (view, state) {
    var self = this;

    // compute the new state
    state = self.base + state.replace(/^\//, '');

    // emit the new state
    view.state.emit(state);
}

function activate (state) {
    var self = this;

    // remove the active class
    $('.active', self.layout.template.dom).removeClass('active');

    // add the active class to the active navigation tab
    if (self.active) {
        $(self.active).addClass('active');
    }
}

function loadModel (config, item, callback) {

    switch (typeof config.model) {

        // model by name
        case 'string':
            item.model(config.model, function (err, model) {
                model.read({q: {}}, function (err, data) {
                    callback(err, data);
                });
            });
            return;

        // model data
        case 'object':
            callback(null, config.model);
            return;

        default:
            callback('Invalid navigation model');
            return;
    }
}

function init () {
    var self = this;
    var config = self.mono.config.data;
    var V = View(self);

    if (!config) {
        return console.error('[nav: no config available]');
    }

    // state handler
    self.activate = activate;

    // set base
    self.baseLength = config.baseLength || 0;
    if (self.baseLength < 0) {
        self.baseLength = 0;
    }
    self.base = location.pathname.split('/').slice(0, self.baseLength + 1).join('/') + '/';

    // get current active
    var active = null;
    if (config.pattern) {
        var path = location.pathname;
        if (path === self.base) {
            active = '/';
        } else {
            active = path.match(new RegExp(config.pattern));
            active = active ? active[1] : null;
        }
    }

    // init layout view
    V.load(config.layout, function (err, layout) {

        if (err) {
            return console.error('[nav: ' + err.toString() + ']');
        }

        // return when no dom is available
        if (!layout.template && layout.template.dom) {
            return console.error('[nav: no dom available]');
        }

        self.layout = layout;

        // render html
        layout.template.render();

        // init item view
        V.load(config.item, function (err, item) {

            if (err) {
                return console.error('[nav: ' + err.toString() + ']');
            }

            // return when no dom is available
            if (!item.template && item.template.dom) {
                return console.error('[nav item: no dom available]');
            }

            loadModel(config, item, function(err, data) {

                if (err) {
                    return console.error('[nav: ' + err.toString() + ']');
                }

                self.item = item;

                // check if stateKey exists
                if (!data[0] || typeof data[0][config.stateKey] === 'undefined') {
                    return;
                }

                // render items
                item.template.render(data);

                // add change state handler to nav items
                var i = 0;
                var items = $('li', item.template.dom);
                for (i = 0; i < items.length; ++i) {
                    var $item = $(items[i]);

                    // save active item if found in the url
                    if (data[i][config.stateKey] === active) {
                        self.active = $item;
                    }

                    $(items[i]).on('click', domStateHandler(self, item, data[i][config.stateKey]));
                }

                // emit state events on dom elms
                if (config.domStates) {
                    for (i = 0; i < config.domStates.length; ++i) {
                        // get element
                        var elm = $(config.domStates[i].selector, layout.template.dom);
                        if (elm) {

                            // save active item if found in the url
                            if (config.domStates[i].state === active){
                                self.active = elm;
                            }

                            elm.on(config.domStates[i].event, domStateHandler(self, item, config.domStates[i].state));
                        }
                    }
                }

                item.state.emit();
            });
        });
    });
}

module.exports = init;

return module; });
<commit_msg>improved navigation<commit_after>M.wrap('github/jillix/navigation/v0.0.1/navigation.js', function (require, module, exports) {

var View = require('github/jillix/view/v0.0.1/view');

// emit dom states
function domStateHandler (self, view, state) {

    // compute the new state
    state = self.base + state.replace(/^\//, '');

    return function () {
        // save current active dom elm
        self.active = this;

        // emit the new state
        view.state.emit(state);
    };
}

function activate (state) {
    var self = this;

    // set active when state is emitted from popstate
    if (self.pattern && state.popstate) {
        var active = getActiveNameFromUrl(self.pattern);
        if (self.items[active]) {
            self.active = self.items[active];
        }
    }

    // remove the active class
    $('.active', self.layout.template.dom).removeClass('active');

    // add the active class to the active navigation tab
    if (self.active) {
        $(self.active).addClass('active');
    }
}

function loadModel (config, item, callback) {

    switch (typeof config.model) {

        // model by name
        case 'string':
            item.model(config.model, function (err, model) {
                model.read({q: {}}, function (err, data) {
                    callback(err, data);
                });
            });
            return;

        // model data
        case 'object':
            callback(null, config.model);
            return;

        default:
            callback('Invalid navigation model');
            return;
    }
}

function getActiveNameFromUrl (pattern) {
    var active;
    var path = location.pathname;
    if (path === self.base) {
        active = '/';
    } else {
        active = path.match(new RegExp(pattern));
        active = active ? active[1] : null;
    }

    return active;
}

function init () {
    var self = this;
    var config = self.mono.config.data;
    var V = View(self);

    if (!config) {
        return console.error('[nav: no config available]');
    }

    // state handler
    self.activate = activate;

    // set base
    config.baseLength = config.baseLength || 0;
    if (config.baseLength < 0) {
        config.baseLength = 0;
    }
    self.base = location.pathname.split('/').slice(0, config.baseLength + 1).join('/') + '/';

    // get current active
    var active = null;
    if (config.pattern) {
        self.pattern = config.pattern;
        active = getActiveNameFromUrl(config.pattern);
    }

    // init layout view
    V.load(config.layout, function (err, layout) {

        if (err) {
            return console.error('[nav: ' + err.toString() + ']');
        }

        // return when no dom is available
        if (!layout.template && layout.template.dom) {
            return console.error('[nav: no dom available]');
        }

        self.layout = layout;

        // render html
        layout.template.render();

        // init item view
        V.load(config.item, function (err, item) {

            if (err) {
                return console.error('[nav: ' + err.toString() + ']');
            }

            // return when no dom is available
            if (!item.template && item.template.dom) {
                return console.error('[nav item: no dom available]');
            }

            loadModel(config, item, function(err, data) {

                if (err) {
                    return console.error('[nav: ' + err.toString() + ']');
                }

                self.item = item;

                // check if stateKey exists
                if (!data[0] || typeof data[0][config.stateKey] === 'undefined') {
                    return;
                }

                // render items
                item.template.render(data);

                // add change state handler to nav items
                var i = 0;
                var items = $('li', item.template.dom);
                self.items = {};

                for (i = 0; i < items.length; ++i) {
                    var $item = $(items[i]);

                    // save state item
                    self.items[data[i][config.stateKey]] = $item;

                    // save active item if found in the url
                    if (data[i][config.stateKey] === active) {
                        self.active = $item;
                    }

                    $item.on('click', domStateHandler(self, item, data[i][config.stateKey]));
                }

                // emit state events on dom elms
                if (config.domStates) {
                    for (i = 0; i < config.domStates.length; ++i) {
                        // get element
                        var elm = $(config.domStates[i].selector, layout.template.dom);
                        if (elm) {

                            // save state items
                            self.items[config.domStates[i].state] = elm;

                            // save active item if found in the url
                            if (config.domStates[i].state === active){
                                self.active = elm;
                            }

                            elm.on(config.domStates[i].event, domStateHandler(self, item, config.domStates[i].state));
                        }
                    }
                }

                item.state.emit();
            });
        });
    });
}

module.exports = init;

return module; });
<|endoftext|>