<commit_before>/**
 * Copyright 2018-present Facebook.
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * @format
 */
import {getInstance as getLogger} from '../fb-stubs/Logger';
import type {Store, MiddlewareAPI} from '../reducers';
import type {DeviceExport} from '../devices/BaseDevice';
import type {State as PluginStates} from '../reducers/pluginStates';
import type {PluginNotification} from '../reducers/notifications.js';
import type {ClientExport} from '../Client.js';
import type {State as PluginStatesState} from '../reducers/pluginStates';
import {pluginKey} from '../reducers/pluginStates';
import {FlipperDevicePlugin, FlipperPlugin, callClient} from '../plugin.js';
import {default as BaseDevice} from '../devices/BaseDevice';
import {default as ArchivedDevice} from '../devices/ArchivedDevice';
import {default as Client} from '../Client';
import {getInstance} from '../fb-stubs/Logger.js';
import fs from 'fs';
import uuid from 'uuid';
import {remote} from 'electron';
import {serialize, deserialize} from './serialization';
import {readCurrentRevision} from './packageMetadata.js';
import {tryCatchReportPlatformFailures} from './metrics';
import {promisify} from 'util';
import promiseTimeout from './promiseTimeout';
import {Idler} from './Idler';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';

export type ExportType = {|
  fileVersion: string,
  flipperReleaseRevision: ?string,
  clients: Array<ClientExport>,
  device: ?DeviceExport,
  store: {
    pluginStates: PluginStates,
    activeNotifications: Array<PluginNotification>,
  },
|};

export function processClients(
  clients: Array<ClientExport>,
  serial: string,
  statusUpdate?: (msg: string) => void,
): Array<ClientExport> {
  statusUpdate &&
    statusUpdate(`Filtering Clients for the device id ${serial}...`);
  const filteredClients = clients.filter(
    client => client.query.device_id === serial,
  );
  return filteredClients;
}

export function pluginsClassMap(
  plugins: PluginStates,
): Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>> {
  const pluginsMap: Map<
    string,
    Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
  > = new Map([]);
  plugins.clientPlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  plugins.devicePlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  return pluginsMap;
}

export function processPluginStates(
  clients: Array<ClientExport>,
  serial: string,
  allPluginStates: PluginStatesState,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  statusUpdate?: (msg: string) => void,
): PluginStatesState {
  let pluginStates = {};
  statusUpdate &&
    statusUpdate('Filtering the plugin states for the filtered Clients...');
  for (const key in allPluginStates) {
    const keyArray = key.split('#');
    const pluginName = keyArray.pop();
    const filteredClients = clients.filter(client => {
      // Remove the last entry related to plugin
      return client.id.includes(keyArray.join('#'));
    });
    if (
      filteredClients.length > 0 ||
      (devicePlugins.has(pluginName) && serial === keyArray[0])
    ) {
      // There need not be any client for device Plugins
      pluginStates = {...pluginStates, [key]: allPluginStates[key]};
    }
  }
  return pluginStates;
}

export function processNotificationStates(
  clients: Array<ClientExport>,
  serial: string,
  allActiveNotifications: Array<PluginNotification>,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  statusUpdate?: (msg: string) => void,
): Array<PluginNotification> {
  statusUpdate &&
    statusUpdate('Filtering the notifications for the filtered Clients...');
  const activeNotifications = allActiveNotifications.filter(notif => {
    const filteredClients = clients.filter(client =>
      notif.client ? client.id.includes(notif.client) : false,
    );
    return (
      filteredClients.length > 0 ||
      (devicePlugins.has(notif.pluginId) && serial === notif.client)
    ); // There need not be any client for device Plugins
  });
  return activeNotifications;
}

const addSaltToDeviceSerial = async (
  salt: string,
  device: BaseDevice,
  clients: Array<ClientExport>,
  pluginStates: PluginStatesState,
  pluginNotification: Array<PluginNotification>,
  statusUpdate?: (msg: string) => void,
): Promise<ExportType> => {
  const {serial} = device;
  const newSerial = salt + '-' + serial;
  const newDevice = new ArchivedDevice(
    newSerial,
    device.deviceType,
    device.title,
    device.os,
    device.getLogs(),
  );
  statusUpdate &&
    statusUpdate('Adding salt to the selected device id in the client data...');
  const updatedClients = clients.map((client: ClientExport) => {
    return {
      ...client,
      id: client.id.replace(serial, newSerial),
      query: {...client.query, device_id: newSerial},
    };
  });

  statusUpdate &&
    statusUpdate(
      'Adding salt to the selected device id in the plugin states...',
    );
  const updatedPluginStates: PluginStatesState = {};
  for (let key in pluginStates) {
    if (!key.includes(serial)) {
      throw new Error(
        `Error while exporting, plugin state (${key}) does not have ${serial} in its key`,
      );
    }
    const pluginData = pluginStates[key];
    key = key.replace(serial, newSerial);
    updatedPluginStates[key] = pluginData;
  }

  statusUpdate &&
    statusUpdate(
      'Adding salt to the selected device id in the notification data...',
    );
  const updatedPluginNotifications = pluginNotification.map(notif => {
    if (!notif.client || !notif.client.includes(serial)) {
      throw new Error(
        `Error while exporting, plugin state (${
          notif.pluginId
        }) does not have ${serial} in it`,
      );
    }
    return {...notif, client: notif.client.replace(serial, newSerial)};
  });
  const revision: ?string = await readCurrentRevision();
  return {
    fileVersion: remote.app.getVersion(),
    flipperReleaseRevision: revision,
    clients: updatedClients,
    device: newDevice.toJSON(),
    store: {
      pluginStates: updatedPluginStates,
      activeNotifications: updatedPluginNotifications,
    },
  };
};

export const processStore = async (
  activeNotifications: Array<PluginNotification>,
  device: ?BaseDevice,
  pluginStates: PluginStatesState,
  clients: Array<ClientExport>,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  salt: string,
  statusUpdate?: (msg: string) => void,
): Promise<?ExportType> => {
  if (device) {
    const {serial} = device;
    const processedClients = processClients(clients, serial, statusUpdate);
    const processedPluginStates = processPluginStates(
      processedClients,
      serial,
      pluginStates,
      devicePlugins,
      statusUpdate,
    );
    const processedActiveNotifications = processNotificationStates(
      processedClients,
      serial,
      activeNotifications,
      devicePlugins,
      statusUpdate,
    );
    // Adding salt to the device id, so that the device_id in the device list is unique.
    const exportFlipperData = await addSaltToDeviceSerial(
      salt,
      device,
      processedClients,
      processedPluginStates,
      processedActiveNotifications,
      statusUpdate,
    );
    return exportFlipperData;
  }
  return null;
};

export async function fetchMetadata(
  pluginStates: PluginStatesState,
  pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>,
  store: MiddlewareAPI,
  statusUpdate?: (msg: string) => void,
): Promise<{pluginStates: PluginStatesState, errorArray: Array<Error>}> {
  const newPluginState = {...pluginStates};
  const errorArray: Array<Error> = [];
  const clients = store.getState().connections.clients;
  const selectedDevice = store.getState().connections.selectedDevice;
  for (const client of clients) {
    if (
      !selectedDevice ||
      selectedDevice.isArchived ||
      !client.id.includes(selectedDevice.serial)
    ) {
      continue;
    }
    for (const plugin of client.plugins) {
      const pluginClass: ?Class<
        FlipperDevicePlugin<> | FlipperPlugin<>,
      > = plugin ? pluginsMap.get(plugin) : null;
      const exportState = pluginClass ? pluginClass.exportPersistedState : null;
      if (exportState) {
        const key = pluginKey(client.id, plugin);
        try {
          statusUpdate &&
            statusUpdate(`Fetching metadata for plugin ${plugin}...`);
          const data = await promiseTimeout(
            120000, // Timeout in 2 mins
            exportState(callClient(client, plugin), newPluginState[key], store),
            `Timed out while collecting data for ${plugin}`,
          );
          newPluginState[key] = data;
        } catch (e) {
          errorArray.push(e);
          continue;
        }
      }
    }
  }
  return {pluginStates: newPluginState, errorArray};
}

export async function getStoreExport(
  store: MiddlewareAPI,
  statusUpdate?: (msg: string) => void,
): Promise<{exportData: ?ExportType, errorArray: Array<Error>}> {
  const state = store.getState();
  const {clients} = state.connections;
  const {pluginStates} = state;
  const {plugins} = state;
  const {selectedDevice} = store.getState().connections;
  if (!selectedDevice) {
    throw new Error('Please select a device before exporting data.');
  }
  // TODO: T39612653 Make Client mockable. Currently rsocket logic is tightly coupled.
  // Not passing the entire state as currently Client is not mockable.

  const pluginsMap: Map<
    string,
    Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
  > = new Map([]);
  plugins.clientPlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  plugins.devicePlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  statusUpdate && statusUpdate('Preparing to fetch metadata from client...');
  const metadata = await fetchMetadata(
    pluginStates,
    pluginsMap,
    store,
    statusUpdate,
  );
  const {errorArray} = metadata;
  const newPluginState = metadata.pluginStates;

  const {activeNotifications} = store.getState().notifications;
  const {devicePlugins} = store.getState().plugins;
  const exportData = await processStore(
    activeNotifications,
    selectedDevice,
    newPluginState,
    clients.map(client => client.toJSON()),
    devicePlugins,
    uuid.v4(),
    statusUpdate,
  );
  return {exportData, errorArray};
}

export function exportStore(
  store: MiddlewareAPI,
  idler?: Idler,
  statusUpdate?: (msg: string) => void,
): Promise<{serializedString: string, errorArray: Array<Error>}> {
  getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
  return new Promise(async (resolve, reject) => {
    try {
      statusUpdate && statusUpdate('Preparing to export Flipper data...');
      const {exportData, errorArray} = await getStoreExport(
        store,
        statusUpdate,
      );
      if (!exportData) {
        console.error('Make sure a device is connected');
        reject(new Error('No device is selected'));
      }
      try {
        statusUpdate && statusUpdate('Serializing Flipper data...');
        const serializedString = await serialize(exportData, idler);
        if (serializedString.length <= 0) {
          reject(new Error('Serialize function returned empty string'));
        }
        resolve({serializedString, errorArray});
      } catch (e) {
        reject(e);
      }
    } catch (e) {
      reject(e);
    }
  });
}

export const exportStoreToFile = (
  exportFilePath: string,
  store: Store,
  idler?: Idler,
  statusUpdate?: (msg: string) => void,
): Promise<{errorArray: Array<Error>}> => {
  return exportStore(store, idler, statusUpdate).then(
    ({serializedString, errorArray}) => {
      return promisify(fs.writeFile)(exportFilePath, serializedString).then(
        () => {
          return {errorArray};
        },
      );
    },
  );
};

export function importDataToStore(data: string, store: Store) {
  getLogger().track('usage', IMPORT_FLIPPER_TRACE_EVENT);
  const json = deserialize(data);
  const {device, clients} = json;
  const {serial, deviceType, title, os, logs} = device;
  const archivedDevice = new ArchivedDevice(
    serial,
    deviceType,
    title,
    os,
    logs ? logs : [],
  );
  const devices = store.getState().connections.devices;
  const matchedDevices = devices.filter(
    availableDevice => availableDevice.serial === serial,
  );
  if (matchedDevices.length > 0) {
    store.dispatch({
      type: 'SELECT_DEVICE',
      payload: matchedDevices[0],
    });
    return;
  }
  store.dispatch({
    type: 'REGISTER_DEVICE',
    payload: archivedDevice,
  });
  store.dispatch({
    type: 'SELECT_DEVICE',
    payload: archivedDevice,
  });

  const {pluginStates} = json.store;
  const keys = Object.keys(pluginStates);
  keys.forEach(key => {
    store.dispatch({
      type: 'SET_PLUGIN_STATE',
      payload: {
        pluginKey: key,
        state: pluginStates[key],
      },
    });
  });
  clients.forEach(client => {
    const clientPlugins = keys
      .filter(key => {
        const arr = key.split('#');
        arr.pop();
        const clientPlugin = arr.join('#');
        return client.id === clientPlugin;
      })
      .map(client => client.split('#').pop());
    store.dispatch({
      type: 'NEW_CLIENT',
      payload: new Client(
        client.id,
        client.query,
        null,
        getInstance(),
        store,
        clientPlugins,
      ),
    });
  });
}

export const importFileToStore = (file: string, store: Store) => {
  fs.readFile(file, 'utf8', (err, data) => {
    if (err) {
      console.error(err);
      return;
    }
    importDataToStore(data, store);
  });
};

export function showOpenDialog(store: Store) {
  remote.dialog.showOpenDialog(
    {
      properties: ['openFile'],
    },
    (files: Array<string>) => {
      if (files !== undefined && files.length > 0) {
        tryCatchReportPlatformFailures(() => {
          importFileToStore(files[0], store);
        }, `${IMPORT_FLIPPER_TRACE_EVENT}:UI`);
      }
    },
  );
}
<commit_msg>Remove duplicate import<commit_after>/**
 * Copyright 2018-present Facebook.
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * @format
 */
import {getInstance as getLogger} from '../fb-stubs/Logger';
import type {Store, MiddlewareAPI} from '../reducers';
import type {DeviceExport} from '../devices/BaseDevice';
import type {State as PluginStates} from '../reducers/pluginStates';
import type {PluginNotification} from '../reducers/notifications.js';
import type {ClientExport} from '../Client.js';
import type {State as PluginStatesState} from '../reducers/pluginStates';
import {pluginKey} from '../reducers/pluginStates';
import {FlipperDevicePlugin, FlipperPlugin, callClient} from '../plugin.js';
import {default as BaseDevice} from '../devices/BaseDevice';
import {default as ArchivedDevice} from '../devices/ArchivedDevice';
import {default as Client} from '../Client';
import fs from 'fs';
import uuid from 'uuid';
import {remote} from 'electron';
import {serialize, deserialize} from './serialization';
import {readCurrentRevision} from './packageMetadata.js';
import {tryCatchReportPlatformFailures} from './metrics';
import {promisify} from 'util';
import promiseTimeout from './promiseTimeout';
import {Idler} from './Idler';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';

export type ExportType = {|
  fileVersion: string,
  flipperReleaseRevision: ?string,
  clients: Array<ClientExport>,
  device: ?DeviceExport,
  store: {
    pluginStates: PluginStates,
    activeNotifications: Array<PluginNotification>,
  },
|};

export function processClients(
  clients: Array<ClientExport>,
  serial: string,
  statusUpdate?: (msg: string) => void,
): Array<ClientExport> {
  statusUpdate &&
    statusUpdate(`Filtering Clients for the device id ${serial}...`);
  const filteredClients = clients.filter(
    client => client.query.device_id === serial,
  );
  return filteredClients;
}

export function pluginsClassMap(
  plugins: PluginStates,
): Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>> {
  const pluginsMap: Map<
    string,
    Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
  > = new Map([]);
  plugins.clientPlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  plugins.devicePlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  return pluginsMap;
}

export function processPluginStates(
  clients: Array<ClientExport>,
  serial: string,
  allPluginStates: PluginStatesState,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  statusUpdate?: (msg: string) => void,
): PluginStatesState {
  let pluginStates = {};
  statusUpdate &&
    statusUpdate('Filtering the plugin states for the filtered Clients...');
  for (const key in allPluginStates) {
    const keyArray = key.split('#');
    const pluginName = keyArray.pop();
    const filteredClients = clients.filter(client => {
      // Remove the last entry related to plugin
      return client.id.includes(keyArray.join('#'));
    });
    if (
      filteredClients.length > 0 ||
      (devicePlugins.has(pluginName) && serial === keyArray[0])
    ) {
      // There need not be any client for device Plugins
      pluginStates = {...pluginStates, [key]: allPluginStates[key]};
    }
  }
  return pluginStates;
}

export function processNotificationStates(
  clients: Array<ClientExport>,
  serial: string,
  allActiveNotifications: Array<PluginNotification>,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  statusUpdate?: (msg: string) => void,
): Array<PluginNotification> {
  statusUpdate &&
    statusUpdate('Filtering the notifications for the filtered Clients...');
  const activeNotifications = allActiveNotifications.filter(notif => {
    const filteredClients = clients.filter(client =>
      notif.client ? client.id.includes(notif.client) : false,
    );
    return (
      filteredClients.length > 0 ||
      (devicePlugins.has(notif.pluginId) && serial === notif.client)
    ); // There need not be any client for device Plugins
  });
  return activeNotifications;
}

const addSaltToDeviceSerial = async (
  salt: string,
  device: BaseDevice,
  clients: Array<ClientExport>,
  pluginStates: PluginStatesState,
  pluginNotification: Array<PluginNotification>,
  statusUpdate?: (msg: string) => void,
): Promise<ExportType> => {
  const {serial} = device;
  const newSerial = salt + '-' + serial;
  const newDevice = new ArchivedDevice(
    newSerial,
    device.deviceType,
    device.title,
    device.os,
    device.getLogs(),
  );
  statusUpdate &&
    statusUpdate('Adding salt to the selected device id in the client data...');
  const updatedClients = clients.map((client: ClientExport) => {
    return {
      ...client,
      id: client.id.replace(serial, newSerial),
      query: {...client.query, device_id: newSerial},
    };
  });

  statusUpdate &&
    statusUpdate(
      'Adding salt to the selected device id in the plugin states...',
    );
  const updatedPluginStates: PluginStatesState = {};
  for (let key in pluginStates) {
    if (!key.includes(serial)) {
      throw new Error(
        `Error while exporting, plugin state (${key}) does not have ${serial} in its key`,
      );
    }
    const pluginData = pluginStates[key];
    key = key.replace(serial, newSerial);
    updatedPluginStates[key] = pluginData;
  }

  statusUpdate &&
    statusUpdate(
      'Adding salt to the selected device id in the notification data...',
    );
  const updatedPluginNotifications = pluginNotification.map(notif => {
    if (!notif.client || !notif.client.includes(serial)) {
      throw new Error(
        `Error while exporting, plugin state (${
          notif.pluginId
        }) does not have ${serial} in it`,
      );
    }
    return {...notif, client: notif.client.replace(serial, newSerial)};
  });
  const revision: ?string = await readCurrentRevision();
  return {
    fileVersion: remote.app.getVersion(),
    flipperReleaseRevision: revision,
    clients: updatedClients,
    device: newDevice.toJSON(),
    store: {
      pluginStates: updatedPluginStates,
      activeNotifications: updatedPluginNotifications,
    },
  };
};

export const processStore = async (
  activeNotifications: Array<PluginNotification>,
  device: ?BaseDevice,
  pluginStates: PluginStatesState,
  clients: Array<ClientExport>,
  devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
  salt: string,
  statusUpdate?: (msg: string) => void,
): Promise<?ExportType> => {
  if (device) {
    const {serial} = device;
    const processedClients = processClients(clients, serial, statusUpdate);
    const processedPluginStates = processPluginStates(
      processedClients,
      serial,
      pluginStates,
      devicePlugins,
      statusUpdate,
    );
    const processedActiveNotifications = processNotificationStates(
      processedClients,
      serial,
      activeNotifications,
      devicePlugins,
      statusUpdate,
    );
    // Adding salt to the device id, so that the device_id in the device list is unique.
    const exportFlipperData = await addSaltToDeviceSerial(
      salt,
      device,
      processedClients,
      processedPluginStates,
      processedActiveNotifications,
      statusUpdate,
    );
    return exportFlipperData;
  }
  return null;
};

export async function fetchMetadata(
  pluginStates: PluginStatesState,
  pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>,
  store: MiddlewareAPI,
  statusUpdate?: (msg: string) => void,
): Promise<{pluginStates: PluginStatesState, errorArray: Array<Error>}> {
  const newPluginState = {...pluginStates};
  const errorArray: Array<Error> = [];
  const clients = store.getState().connections.clients;
  const selectedDevice = store.getState().connections.selectedDevice;
  for (const client of clients) {
    if (
      !selectedDevice ||
      selectedDevice.isArchived ||
      !client.id.includes(selectedDevice.serial)
    ) {
      continue;
    }
    for (const plugin of client.plugins) {
      const pluginClass: ?Class<
        FlipperDevicePlugin<> | FlipperPlugin<>,
      > = plugin ? pluginsMap.get(plugin) : null;
      const exportState = pluginClass ? pluginClass.exportPersistedState : null;
      if (exportState) {
        const key = pluginKey(client.id, plugin);
        try {
          statusUpdate &&
            statusUpdate(`Fetching metadata for plugin ${plugin}...`);
          const data = await promiseTimeout(
            120000, // Timeout in 2 mins
            exportState(callClient(client, plugin), newPluginState[key], store),
            `Timed out while collecting data for ${plugin}`,
          );
          newPluginState[key] = data;
        } catch (e) {
          errorArray.push(e);
          continue;
        }
      }
    }
  }
  return {pluginStates: newPluginState, errorArray};
}

export async function getStoreExport(
  store: MiddlewareAPI,
  statusUpdate?: (msg: string) => void,
): Promise<{exportData: ?ExportType, errorArray: Array<Error>}> {
  const state = store.getState();
  const {clients} = state.connections;
  const {pluginStates} = state;
  const {plugins} = state;
  const {selectedDevice} = store.getState().connections;
  if (!selectedDevice) {
    throw new Error('Please select a device before exporting data.');
  }
  // TODO: T39612653 Make Client mockable. Currently rsocket logic is tightly coupled.
  // Not passing the entire state as currently Client is not mockable.

  const pluginsMap: Map<
    string,
    Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
  > = new Map([]);
  plugins.clientPlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  plugins.devicePlugins.forEach((val, key) => {
    pluginsMap.set(key, val);
  });
  statusUpdate && statusUpdate('Preparing to fetch metadata from client...');
  const metadata = await fetchMetadata(
    pluginStates,
    pluginsMap,
    store,
    statusUpdate,
  );
  const {errorArray} = metadata;
  const newPluginState = metadata.pluginStates;

  const {activeNotifications} = store.getState().notifications;
  const {devicePlugins} = store.getState().plugins;
  const exportData = await processStore(
    activeNotifications,
    selectedDevice,
    newPluginState,
    clients.map(client => client.toJSON()),
    devicePlugins,
    uuid.v4(),
    statusUpdate,
  );
  return {exportData, errorArray};
}

export function exportStore(
  store: MiddlewareAPI,
  idler?: Idler,
  statusUpdate?: (msg: string) => void,
): Promise<{serializedString: string, errorArray: Array<Error>}> {
  getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
  return new Promise(async (resolve, reject) => {
    try {
      statusUpdate && statusUpdate('Preparing to export Flipper data...');
      const {exportData, errorArray} = await getStoreExport(
        store,
        statusUpdate,
      );
      if (!exportData) {
        console.error('Make sure a device is connected');
        reject(new Error('No device is selected'));
      }
      try {
        statusUpdate && statusUpdate('Serializing Flipper data...');
        const serializedString = await serialize(exportData, idler);
        if (serializedString.length <= 0) {
          reject(new Error('Serialize function returned empty string'));
        }
        resolve({serializedString, errorArray});
      } catch (e) {
        reject(e);
      }
    } catch (e) {
      reject(e);
    }
  });
}

export const exportStoreToFile = (
  exportFilePath: string,
  store: Store,
  idler?: Idler,
  statusUpdate?: (msg: string) => void,
): Promise<{errorArray: Array<Error>}> => {
  return exportStore(store, idler, statusUpdate).then(
    ({serializedString, errorArray}) => {
      return promisify(fs.writeFile)(exportFilePath, serializedString).then(
        () => {
          return {errorArray};
        },
      );
    },
  );
};

export function importDataToStore(data: string, store: Store) {
  getLogger().track('usage', IMPORT_FLIPPER_TRACE_EVENT);
  const json = deserialize(data);
  const {device, clients} = json;
  const {serial, deviceType, title, os, logs} = device;
  const archivedDevice = new ArchivedDevice(
    serial,
    deviceType,
    title,
    os,
    logs ? logs : [],
  );
  const devices = store.getState().connections.devices;
  const matchedDevices = devices.filter(
    availableDevice => availableDevice.serial === serial,
  );
  if (matchedDevices.length > 0) {
    store.dispatch({
      type: 'SELECT_DEVICE',
      payload: matchedDevices[0],
    });
    return;
  }
  store.dispatch({
    type: 'REGISTER_DEVICE',
    payload: archivedDevice,
  });
  store.dispatch({
    type: 'SELECT_DEVICE',
    payload: archivedDevice,
  });

  const {pluginStates} = json.store;
  const keys = Object.keys(pluginStates);
  keys.forEach(key => {
    store.dispatch({
      type: 'SET_PLUGIN_STATE',
      payload: {
        pluginKey: key,
        state: pluginStates[key],
      },
    });
  });
  clients.forEach(client => {
    const clientPlugins = keys
      .filter(key => {
        const arr = key.split('#');
        arr.pop();
        const clientPlugin = arr.join('#');
        return client.id === clientPlugin;
      })
      .map(client => client.split('#').pop());
    store.dispatch({
      type: 'NEW_CLIENT',
      payload: new Client(
        client.id,
        client.query,
        null,
        getLogger(),
        store,
        clientPlugins,
      ),
    });
  });
}

export const importFileToStore = (file: string, store: Store) => {
  fs.readFile(file, 'utf8', (err, data) => {
    if (err) {
      console.error(err);
      return;
    }
    importDataToStore(data, store);
  });
};

export function showOpenDialog(store: Store) {
  remote.dialog.showOpenDialog(
    {
      properties: ['openFile'],
    },
    (files: Array<string>) => {
      if (files !== undefined && files.length > 0) {
        tryCatchReportPlatformFailures(() => {
          importFileToStore(files[0], store);
        }, `${IMPORT_FLIPPER_TRACE_EVENT}:UI`);
      }
    },
  );
}
<|endoftext|>


<commit_before>/*!
 * Forked from vdom-virtualize@0.5.1 by Marcel Klehr <mklehr@gmx.net>
 *
 * (MIT LICENSE)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the 'Software'), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
'use strict';

var _ = require('underscore');
var VNode = require('virtual-dom/vnode/vnode');
var VText = require('virtual-dom/vnode/vtext');

/**
 * DOMNode property white list
 * Taken from https://github.com/Raynos/react/blob/dom-property-config/src/browser/ui/dom/DefaultDOMPropertyConfig.js
 */
var props = [
  'accept', 'accessKey', 'action', 'alt', 'async', 'autoComplete', 'autoPlay', 'cellPadding',
  'cellSpacing', 'checked', 'className', 'colSpan', 'content', 'contentEditable', 'controls',
  'crossOrigin', 'data', 'dataset', 'defer', 'dir', 'download', 'draggable', 'encType', 'formNoValidate',
  'href', 'hrefLang', 'htmlFor', 'httpEquiv', 'icon', 'id', 'label', 'lang', 'list', 'loop', 'max',
  'mediaGroup', 'method', 'min', 'multiple', 'muted', 'name', 'noValidate', 'pattern', 'placeholder',
  'poster', 'preload', 'radioGroup', 'readOnly', 'rel', 'required', 'rowSpan', 'sandbox', 'scope',
  'scrollLeft', 'scrolling', 'scrollTop', 'selected', 'span', 'spellCheck', 'src', 'srcDoc', 'srcSet',
  'start', 'step', 'style', 'tabIndex', 'target', 'title', 'type', 'value',

  // Non-standard Properties
  'autoCapitalize', 'autoCorrect', 'property', 'attributes'
];

function getElementProperties(el) {
  var obj = {};

  _.forEach(props, function(propName) {
    if (!el[propName]) {
      return;
    }

    // Special case: style
    // .style is a DOMStyleDeclaration, thus we need to iterate over all
    // rules to create a hash of applied css properties.
    //
    // You can directly set a specific .style[prop] = value so patching with vdom
    // is possible.
    if ("style" == propName) {
      var css = {}
        , styleProp;
      for (var i = 0; i < el.style.length; i++) {
        styleProp = el.style[i];
        css[styleProp] = el.style.getPropertyValue(styleProp); // XXX: add support for "!important" via getPropertyPriority()!
      }

      obj[propName] = css;
      return
    }

    // Special case: dataset
    // we can iterate over .dataset with a simple for..in loop.
    // The all-time foo with data-* attribs is the dash-snake to camelCase
    // conversion.
    // However, I'm not sure if this is compatible with h()
    //
    // .dataset properties are directly accessible as transparent getters/setters, so
    // patching with vdom is possible.
    if ("dataset" == propName) {
      var data = {};
      for (var p in el.dataset) {
        data[p] = el.dataset[p]
      }

      obj[propName] = data;
      return
    }

    // Special case: attributes
    // some properties are only accessible via .attributes, so
    // that's what we'd do, if vdom-create-element could handle this.
    if ("attributes" == propName) {
      return;
    }
    if ("tabIndex" == propName && el.tabIndex === -1) {
      return;
    }

    // default: just copy the property
    obj[propName] = el[propName];
    return;
  });

  return obj
}

function createFromTextNode(tNode) {
  return new VText(tNode.nodeValue);
}

function createFromElement(el) {
  var tagName = el.tagName,
    namespace = el.namespaceURI === 'http://www.w3.org/1999/xhtml' ? null : el.namespaceURI,
    properties = getElementProperties(el),
    children = [];

  for (var i = 0; i < el.childNodes.length; i++) {
    children.push(createVNode(el.childNodes[i]));
  }

  var vnode = new VNode(tagName, properties, children, null, namespace);
  return vnode;
}

function createVNode(domNode) {
  if (domNode.nodeType === 1) {
    return createFromElement(domNode);
  }
  if (domNode.nodeType === 3) {
    return createFromTextNode(domNode);
  }
}

module.exports = createVNode;
<commit_msg>Double to single quotes styleguide fix<commit_after>/*!
 * Forked from vdom-virtualize@0.5.1 by Marcel Klehr <mklehr@gmx.net>
 *
 * (MIT LICENSE)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the 'Software'), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
'use strict';

var _ = require('underscore');
var VNode = require('virtual-dom/vnode/vnode');
var VText = require('virtual-dom/vnode/vtext');

/**
 * DOMNode property white list
 * Taken from https://github.com/Raynos/react/blob/dom-property-config/src/browser/ui/dom/DefaultDOMPropertyConfig.js
 */
var props = [
  'accept', 'accessKey', 'action', 'alt', 'async', 'autoComplete', 'autoPlay', 'cellPadding',
  'cellSpacing', 'checked', 'className', 'colSpan', 'content', 'contentEditable', 'controls',
  'crossOrigin', 'data', 'dataset', 'defer', 'dir', 'download', 'draggable', 'encType', 'formNoValidate',
  'href', 'hrefLang', 'htmlFor', 'httpEquiv', 'icon', 'id', 'label', 'lang', 'list', 'loop', 'max',
  'mediaGroup', 'method', 'min', 'multiple', 'muted', 'name', 'noValidate', 'pattern', 'placeholder',
  'poster', 'preload', 'radioGroup', 'readOnly', 'rel', 'required', 'rowSpan', 'sandbox', 'scope',
  'scrollLeft', 'scrolling', 'scrollTop', 'selected', 'span', 'spellCheck', 'src', 'srcDoc', 'srcSet',
  'start', 'step', 'style', 'tabIndex', 'target', 'title', 'type', 'value',

  // Non-standard Properties
  'autoCapitalize', 'autoCorrect', 'property', 'attributes'
];

function getElementProperties(el) {
  var obj = {};

  _.forEach(props, function(propName) {
    if (!el[propName]) {
      return;
    }

    // Special case: style
    // .style is a DOMStyleDeclaration, thus we need to iterate over all
    // rules to create a hash of applied css properties.
    //
    // You can directly set a specific .style[prop] = value so patching with vdom
    // is possible.
    if ('style' == propName) {
      var css = {}
        , styleProp;
      for (var i = 0; i < el.style.length; i++) {
        styleProp = el.style[i];
        css[styleProp] = el.style.getPropertyValue(styleProp); // XXX: add support for '!important' via getPropertyPriority()!
      }

      obj[propName] = css;
      return
    }

    // Special case: dataset
    // we can iterate over .dataset with a simple for..in loop.
    // The all-time foo with data-* attribs is the dash-snake to camelCase
    // conversion.
    // However, I'm not sure if this is compatible with h()
    //
    // .dataset properties are directly accessible as transparent getters/setters, so
    // patching with vdom is possible.
    if ('dataset' == propName) {
      var data = {};
      for (var p in el.dataset) {
        data[p] = el.dataset[p]
      }

      obj[propName] = data;
      return
    }

    // Special case: attributes
    // some properties are only accessible via .attributes, so
    // that's what we'd do, if vdom-create-element could handle this.
    if ('attributes' == propName) {
      return;
    }
    if ('tabIndex' == propName && el.tabIndex === -1) {
      return;
    }

    // default: just copy the property
    obj[propName] = el[propName];
    return;
  });

  return obj
}

function createFromTextNode(tNode) {
  return new VText(tNode.nodeValue);
}

function createFromElement(el) {
  var tagName = el.tagName,
    namespace = el.namespaceURI === 'http://www.w3.org/1999/xhtml' ? null : el.namespaceURI,
    properties = getElementProperties(el),
    children = [];

  for (var i = 0; i < el.childNodes.length; i++) {
    children.push(createVNode(el.childNodes[i]));
  }

  var vnode = new VNode(tagName, properties, children, null, namespace);
  return vnode;
}

function createVNode(domNode) {
  if (domNode.nodeType === 1) {
    return createFromElement(domNode);
  }
  if (domNode.nodeType === 3) {
    return createFromTextNode(domNode);
  }
}

module.exports = createVNode;
<|endoftext|>


<commit_before>import { h } from 'preact' /** @jsx h */
import { MenuTrigger } from '../../menu/components'
import { LayoutDefault, Footer } from '../layout/layout'
import {
  ButtonPrimary,
  OrnamentPageFooter,
  ParagraphLineRounded,
  SectionHeader,
  SectionPrimary,
} from '../components'
import { FeaturedMemories } from '../sections'
import * as paths from '../../paths'

export default () => (
  <LayoutDefault>

        <MenuTrigger />

          Vamos usar a memória como ferramenta para mudança. Homenageando
          aqueles que estão #VivosEmNós, podemos transformar saudade
          em mobilização e, juntos, lutar por mais respeito à vida.

        <ButtonPrimary href={paths.memoryCreate()}>
          Crie sua homenagem
        </ButtonPrimary>

      <SectionPrimary
        className="section--about-campaign"
        header={<SectionHeader title={Sobre
a campanha} />}
      >

          <ParagraphLineRounded>
            No Brasil, a cada duas pessoas, uma conhece
            alguém que foi assassinado
          </ParagraphLineRounded>

          As estatísticas assustam, mas os números não nos permitem enxergar o mais importante:
          os nomes, os rostos e as histórias dessas vítimas. Tratá-las como dígitos nos
          relatórios de segurança pública nos afasta da real dimensão do problema e nos
          impede de cobrar soluções efetivas.

          #VivosEmNós quer usar a memória como ferramenta
          para transformar solidariedade em união, saudade em mobilização e indignação em força
          para mudança. Se você conhece alguém que teve a sua história interrompida pela violência,
          crie aqui uma página de homenagem para se juntar à luta por mais respeito à vida.

      </SectionPrimary>

      <FeaturedMemories />

      <SectionPrimary
        className="section--about-us"
        header={<SectionHeader title={Quem
somos} />}
      >

          #VivosEmNós é uma iniciativa da campanha
          Instinto de Vida, uma aliança latino-americana criada a partir da união de organizações
          não governamentais que compartilham um objetivo: reduzir à metade a violência letal
          na região em 10 anos.

          Essa meta requer muito foco, compromisso e pressão por políticas públicas
          efetivas. #VivosEmNós é o espaço que criamos
          para homenagear as pessoas levadas precocemente pela violência e unir todos que
          desejam lutar pelo respeito à vida. A sensibilização é o primeiro passo em direção
          à mudança.

      </SectionPrimary>

          Fale conosco:

            contato@instintodevida.org

        <OrnamentPageFooter />

  </LayoutDefault>
)
<commit_msg>chore(home-campaign): add the background silhouette #2<commit_after>import { h } from 'preact' /** @jsx h */
import { MenuTrigger } from '../../menu/components'
import { LayoutDefault, Footer } from '../layout/layout'
import {
  ButtonPrimary,
  OrnamentPageFooter,
  ParagraphLineRounded,
  SectionHeader,
  SectionPrimary,
  Silhouette,
} from '../components'
import { FeaturedMemories } from '../sections'
import * as paths from '../../paths'

export default () => (
  <LayoutDefault>

        <MenuTrigger />

          Vamos usar a memória como ferramenta para mudança. Homenageando
          aqueles que estão #VivosEmNós, podemos transformar saudade
          em mobilização e, juntos, lutar por mais respeito à vida.

        <ButtonPrimary href={paths.memoryCreate()}>
          Crie sua homenagem
        </ButtonPrimary>

      <SectionPrimary
        className="section--about-campaign"
        header={
          <SectionHeader title={Sobre
a campanha}>
            <Silhouette variation="1" height="570" />
          </SectionHeader>
        }
      >

          <ParagraphLineRounded>
            No Brasil, a cada duas pessoas, uma conhece
            alguém que foi assassinado
          </ParagraphLineRounded>

          As estatísticas assustam, mas os números não nos permitem enxergar o mais importante:
          os nomes, os rostos e as histórias dessas vítimas. Tratá-las como dígitos nos
          relatórios de segurança pública nos afasta da real dimensão do problema e nos
          impede de cobrar soluções efetivas.

          #VivosEmNós quer usar a memória como ferramenta
          para transformar solidariedade em união, saudade em mobilização e indignação em força
          para mudança. Se você conhece alguém que teve a sua história interrompida pela violência,
          crie aqui uma página de homenagem para se juntar à luta por mais respeito à vida.

      </SectionPrimary>

      <FeaturedMemories />

      <SectionPrimary
        className="section--about-us"
        header={<SectionHeader title={Quem
somos} />}
      >

          #VivosEmNós é uma iniciativa da campanha
          Instinto de Vida, uma aliança latino-americana criada a partir da união de organizações
          não governamentais que compartilham um objetivo: reduzir à metade a violência letal
          na região em 10 anos.

          Essa meta requer muito foco, compromisso e pressão por políticas públicas
          efetivas. #VivosEmNós é o espaço que criamos
          para homenagear as pessoas levadas precocemente pela violência e unir todos que
          desejam lutar pelo respeito à vida. A sensibilização é o primeiro passo em direção
          à mudança.

      </SectionPrimary>

          Fale conosco:

            contato@instintodevida.org

        <OrnamentPageFooter />

  </LayoutDefault>
)
<|endoftext|>


<commit_before>import engine from './voxelEngine';
import compression from './compression';
import extend from 'extend';
import storage from './storage';
import consts from '../constants';
import auth from '../auth';

function compress(chunk) {
  let compressedChunk = extend({}, chunk);
  compressedChunk.voxels = compression.compress(chunk.position, chunk.voxels);
  return compressedChunk;
}

let pendingChanges = [];

export default {
  _scheduleDatabaseSave() {
    setTimeout(async () => {
      try {
        await this.saveChunks();
      } catch(err) {
        console.log('Error auto saving chunks', err);
      }
    }, consts.voxel.AUTO_SAVE_INTERVAL);
  },
  async init(dbConn) {
    this._dbConn = dbConn;

    engine.init();

    let chunks = await storage.getChunks(this._dbConn);
    for(let chunk of chunks) {
      chunk.existsInDatabase = true;
    }
    engine.setManyChunks(chunks);

    this._scheduleDatabaseSave();

    return chunks.length;
  },
  async initClient(dbConn) {
    let materials = (await storage.getMaterials(dbConn)).sort((m1, m2) => m1.id > m2.id).map(m => m.textures);
    return {
      settings: extend({}, engine.getSettings(), {
        materials,
        initialPosition: consts.playerSync.AVATAR_INITIAL_POSITION
      })
    };
  },
  requestChunk(chunkPos) {
    engine.ensureChunkExists(chunkPos);

    let chunk = engine.getChunk(chunkPos);
    return compress(chunk);
  },
  async set(token, pos, val, broadcast) {
    let chunkPos = engine.chunkAtPosition(pos);
    let chunk = engine.getChunk(chunkPos);

    let user = await auth.getUser(token);
    let userId = user ? user.id : null;

    if(userId == chunk.owner) {
      console.log('saving')
      engine.setBlock(pos, val);
      compression.invalidateCache(chunkPos);
      pendingChanges.push({pos, val, chunkDims: chunk.dims, chunkPos});
      broadcast(pos, val);
      console.log('ok')
    } else {
      throw new Error(`User ${userId} does not have access to chunk at ${chunkPos}`);
    }
  },
  async saveChunks() {
    let changes = pendingChanges.splice(0, pendingChanges.length);

    if(changes.length) {
      console.log(`Saving ${changes.length} chunk changes...`);
    }

    let addedChunks = [];

    for(let change of changes) {
      try {
        if(!engine.existsInDatabase(change.chunkPos)) {
          let chunk = engine.getChunk(change.chunkPos);
          console.log('chunk', chunk.owner)
          await storage.saveChunk(this._dbConn, chunk);
          engine.markAsExistingInDatabase(change.chunkPos);
          addedChunks.push(change.chunkPos);
          console.log(`Chunk at ${chunk.id} didn't exist in database, so we're adding it`);
        } else {
          // We're not sending the delta if we just sent the whole chunk
          if(!addedChunks.includes(change.chunkPos)) {
            await storage.saveChunkChange(this._dbConn, change.chunkPos, change.chunkDims, change.pos, change.val);
          }
        }
      } catch(err) {
        console.log('Error saving chunk change', err);
        pendingChanges.unshift(change);
      }
    }

    this._scheduleDatabaseSave();
  }
};
<commit_msg>remvoing logs<commit_after>import engine from './voxelEngine';
import compression from './compression';
import extend from 'extend';
import storage from './storage';
import consts from '../constants';
import auth from '../auth';

function compress(chunk) {
  let compressedChunk = extend({}, chunk);
  compressedChunk.voxels = compression.compress(chunk.position, chunk.voxels);
  return compressedChunk;
}

let pendingChanges = [];

export default {
  _scheduleDatabaseSave() {
    setTimeout(async () => {
      try {
        await this.saveChunks();
      } catch(err) {
        console.log('Error auto saving chunks', err);
      }
    }, consts.voxel.AUTO_SAVE_INTERVAL);
  },
  async init(dbConn) {
    this._dbConn = dbConn;

    engine.init();

    let chunks = await storage.getChunks(this._dbConn);
    for(let chunk of chunks) {
      chunk.existsInDatabase = true;
    }
    engine.setManyChunks(chunks);

    this._scheduleDatabaseSave();

    return chunks.length;
  },
  async initClient(dbConn) {
    let materials = (await storage.getMaterials(dbConn)).sort((m1, m2) => m1.id > m2.id).map(m => m.textures);
    return {
      settings: extend({}, engine.getSettings(), {
        materials,
        initialPosition: consts.playerSync.AVATAR_INITIAL_POSITION
      })
    };
  },
  requestChunk(chunkPos) {
    engine.ensureChunkExists(chunkPos);

    let chunk = engine.getChunk(chunkPos);
    return compress(chunk);
  },
  async set(token, pos, val, broadcast) {
    let chunkPos = engine.chunkAtPosition(pos);
    let chunk = engine.getChunk(chunkPos);

    let user = await auth.getUser(token);
    let userId = user ? user.id : null;

    if(userId == chunk.owner) {
      engine.setBlock(pos, val);
      compression.invalidateCache(chunkPos);
      pendingChanges.push({pos, val, chunkDims: chunk.dims, chunkPos});
      broadcast(pos, val);
    } else {
      throw new Error(`User ${userId} does not have access to chunk at ${chunkPos}`);
    }
  },
  async saveChunks() {
    let changes = pendingChanges.splice(0, pendingChanges.length);

    if(changes.length) {
      console.log(`Saving ${changes.length} chunk changes...`);
    }

    let addedChunks = [];

    for(let change of changes) {
      try {
        if(!engine.existsInDatabase(change.chunkPos)) {
          let chunk = engine.getChunk(change.chunkPos);
          await storage.saveChunk(this._dbConn, chunk);
          engine.markAsExistingInDatabase(change.chunkPos);
          addedChunks.push(change.chunkPos);
          console.log(`Chunk at ${chunk.id} didn't exist in database, so we're adding it`);
        } else {
          // We're not sending the delta if we just sent the whole chunk
          if(!addedChunks.includes(change.chunkPos)) {
            await storage.saveChunkChange(this._dbConn, change.chunkPos, change.chunkDims, change.pos, change.val);
          }
        }
      } catch(err) {
        console.log('Error saving chunk change', err);
        pendingChanges.unshift(change);
      }
    }

    this._scheduleDatabaseSave();
  }
};
<|endoftext|>


<commit_before>/*global describe, beforeEach, module, inject, it, spyOn, expect, $, console */

describe('dfTagList', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if there is no items attribute', function() {
      function compile() {
        $compile('

')(scope);
      }

      expect(compile).toThrow();
    });
  });
});

describe('dfTagAdd', function() {
  'use strict';

  var scope, $compile, $document;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should construct proper DOM structure', function() {
      scope.items = ['foo'];
      scope.$digest();
      var element = $compile('

')(scope);
      expect(element.html()).toBe('<form><input placeholder="Tag"></form><button class="add"></button>');
    });
  });
  describe('when the tags array begins empty', function() {
    it('should show the input and hide the + button', function() {
      scope.items = [];
      var element = $compile('

')(scope);
      scope.$digest();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button').css('display')).toBe('none');
    });
  });
  describe('when the tags array is emptied (but does not begin empty)', function() {
    it('should show the input and hide the + button', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      scope.items.pop();
      scope.$digest();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button').css('display')).toBe('none');
    });
  });
  describe('after clicking +', function() {
    it('should hide the + button and show the input', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      element.find('button.add').click();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button.add').css('display')).toBe('none');
    });
    it('de-focusing the input should clear and hide the input and show the + button (after a timeout)', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      element.find('button.add').click();
      element.find('input').val('bar');
      element.find('input').blur();
      expect(element.find('input').val()).toBe('');
      expect(element.find('input').is(':visible')).toBe(false);

      // TODO: This happens asynchronously now (in a $timeout), so this
      // synchronous test is failing.
      // expect(element.find('button.add').css('display')).toNotBe('none');
    });
  });
  describe('after submitting input value', function() {
    it('should append value', function() {
      scope.items = ['foo'];
      var element = $compile('
<form><input><button id="submit123">submit</button></form>
')(scope);
      scope.$digest();
      element.find('input').val('bar');
      element.find('#submit123').click();
      expect(scope.items).toEqual(['foo','bar']);
    });
  });
});

describe('dfTag', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if we have no $scope.items', function() {
      function compile() {
        $compile('
foo
')(scope);
      }

      expect(compile).toThrow();
    });
    it('should construct proper DOM structure', function() {
      scope.items = ['foo'];
      scope.$digest();
      var element = $compile('
bar
')(scope);
      expect(element.html()).toBe('bar<button class="remove"></button>');
    });
  });
  describe('clicking on the remove button', function() {
    it('should remove the tag from the parent scope tags array', function() {
      scope.items = ['foo'];
      var element = $compile('
bar
')(scope);
      scope.$digest();
      element.find('button.remove').click();
      expect(scope.items).toEqual([]);
    });
  });
});
<commit_msg>Use existing elements<commit_after>/*global describe, beforeEach, module, inject, it, spyOn, expect, $, console */

describe('dfTagList', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if there is no items attribute', function() {
      function compile() {
        $compile('

')(scope);
      }

      expect(compile).toThrow();
    });
  });
});

describe('dfTagAdd', function() {
  'use strict';

  var scope, $compile, $document;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should construct proper DOM structure', function() {
      scope.items = ['foo'];
      scope.$digest();
      var element = $compile('

')(scope);
      expect(element.html()).toBe('<form><input placeholder="Tag"></form><button class="add"></button>');
    });
  });
  describe('when the tags array begins empty', function() {
    it('should show the input and hide the + button', function() {
      scope.items = [];
      var element = $compile('

')(scope);
      scope.$digest();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button').css('display')).toBe('none');
    });
  });
  describe('when the tags array is emptied (but does not begin empty)', function() {
    it('should show the input and hide the + button', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      scope.items.pop();
      scope.$digest();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button').css('display')).toBe('none');
    });
  });
  describe('after clicking +', function() {
    it('should hide the + button and show the input', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      element.find('button.add').click();
      expect(element.find('input').css('display')).toBe('');
      expect(element.find('button.add').css('display')).toBe('none');
    });
    it('de-focusing the input should clear and hide the input and show the + button (after a timeout)', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      element.find('button.add').click();
      element.find('input').val('bar');
      element.find('input').blur();
      expect(element.find('input').val()).toBe('');
      expect(element.find('input').is(':visible')).toBe(false);

      // TODO: This happens asynchronously now (in a $timeout), so this
      // synchronous test is failing.
      // expect(element.find('button.add').css('display')).toNotBe('none');
    });
  });
  describe('after submitting input value', function() {
    it('should append value', function() {
      scope.items = ['foo'];
      var element = $compile('

')(scope);
      scope.$digest();
      element.find('input').val('bar');
      element.find('form').trigger('submit');
      expect(scope.items).toEqual(['foo','bar']);
    });
  });
});

describe('dfTag', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if we have no $scope.items', function() {
      function compile() {
        $compile('
foo
')(scope);
      }

      expect(compile).toThrow();
    });
    it('should construct proper DOM structure', function() {
      scope.items = ['foo'];
      scope.$digest();
      var element = $compile('
bar
')(scope);
      expect(element.html()).toBe('bar<button class="remove"></button>');
    });
  });
  describe('clicking on the remove button', function() {
    it('should remove the tag from the parent scope tags array', function() {
      scope.items = ['foo'];
      var element = $compile('
bar
')(scope);
      scope.$digest();
      element.find('button.remove').click();
      expect(scope.items).toEqual([]);
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Analyzes DOM complexity
 */
exports.version = '0.2';

exports.module = function(phantomas) {

	// HTML size
	phantomas.on('report', function() {
		phantomas.setMetricEvaluate('bodyHTMLSize', function() {
			return document.body && document.body.innerHTML.length || 0;
		});

		phantomas.evaluate(function() {
			(function(phantomas) {
				var runner = new phantomas.nodeRunner(),
					whitespacesRegExp = /^\s+$/;

				// include all nodes
				runner.isSkipped = function(node) {
					return false;
				};

				runner.walk(document.body, function(node, depth) {
					switch (node.nodeType) {
						case Node.COMMENT_NODE:
							phantomas.incr('commentsSize', node.textContent.length + 7); // '<!--' + '-->'.length
							break;

						case Node.ELEMENT_NODE:
							phantomas.incr('DOMelementsCount');
							phantomas.set('DOMelementMaxDepth', Math.max(phantomas.get('DOMelementMaxDepth') || 0, depth));

							// ignore inline <script> tags
							if (node.nodeName === 'SCRIPT') {
								return false;
							}

							// @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
							var styles = window.getComputedStyle(node),
								size = 0;

							if (styles && styles.getPropertyValue('display') === 'none') {
								if (typeof node.innerHTML === 'string') {
									size = node.innerHTML.length;
									phantomas.incr('hiddenContentSize', size);

									// log hidden containers bigger than 1 kB
									if (size > 1024) {
										phantomas.addOffender('hiddenContentSize', phantomas.getDOMPath(node) + ' (' + size + ' bytes)');
									}
								}

								// don't run for child nodes as they're hidden as well
								return false;
							}

							// count nodes with inline CSS
							if (node.hasAttribute('style')) {
								phantomas.incr('nodesWithInlineCSS');
							}

							break;

						case Node.TEXT_NODE:
							if (whitespacesRegExp.test(node.textContent)) {
								phantomas.incr('whiteSpacesSize', node.textContent.length);
							}
							break;
					}
				});

				phantomas.spyEnabled(false, 'counting iframes and images');

				// count <iframe> tags
				phantomas.setMetric('iframesCount', document.querySelectorAll('iframe').length);

				// <img> nodes without dimensions (one of width / height missing)
				phantomas.setMetric('imagesWithoutDimensions', (function() {
					var imgNodes = document.body && document.body.querySelectorAll('img') || [],
						node,
						imagesWithoutDimensions = 0;

					for (var i=0, len=imgNodes.length; i<len; i++) {
						node = imgNodes[i];
						if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
							phantomas.log('Image without dimensions: ' + phantomas.getDOMPath(node));
							imagesWithoutDimensions++;
						}
					}

					return imagesWithoutDimensions;
				})());

				phantomas.spyEnabled(true);
			}(window.__phantomas));
		});

		// total length of HTML comments (including <!-- --> brackets)
		phantomas.setMetricFromScope('commentsSize');

		// total length of HTML of hidden elements (i.e. display: none)
		phantomas.setMetricFromScope('hiddenContentSize');

		// total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
		phantomas.setMetricFromScope('whiteSpacesSize');

		// count all tags
		phantomas.setMetricFromScope('DOMelementsCount');
		phantomas.setMetricFromScope('DOMelementMaxDepth');

		// nodes with inlines CSS (style attribute)
		phantomas.setMetricFromScope('nodesWithInlineCSS');
	});
};
<commit_msg>domComplexity: add offenders for commentsSize, hiddenContentSize, nodesWithInlineCSS and imagesWithoutDimensions metrics<commit_after>/**
 * Analyzes DOM complexity
 */
exports.version = '0.2';

exports.module = function(phantomas) {

	// HTML size
	phantomas.on('report', function() {
		phantomas.setMetricEvaluate('bodyHTMLSize', function() {
			return document.body && document.body.innerHTML.length || 0;
		});

		phantomas.evaluate(function() {
			(function(phantomas) {
				var runner = new phantomas.nodeRunner(),
					whitespacesRegExp = /^\s+$/,
					size = 0;

				// include all nodes
				runner.isSkipped = function(node) {
					return false;
				};

				runner.walk(document.body, function(node, depth) {
					switch (node.nodeType) {
						case Node.COMMENT_NODE:
							size = node.textContent.length + 7; // '<!--' + '-->'.length
							phantomas.incr('commentsSize', size);

							// log HTML comments bigger than 64 characters
							if (size > 64) {
								phantomas.addOffender('commentsSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
							}
							break;

						case Node.ELEMENT_NODE:
							phantomas.incr('DOMelementsCount');
							phantomas.set('DOMelementMaxDepth', Math.max(phantomas.get('DOMelementMaxDepth') || 0, depth));

							// ignore inline <script> tags
							if (node.nodeName === 'SCRIPT') {
								return false;
							}

							// @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
							var styles = window.getComputedStyle(node);

							if (styles && styles.getPropertyValue('display') === 'none') {
								if (typeof node.innerHTML === 'string') {
									size = node.innerHTML.length;
									phantomas.incr('hiddenContentSize', size);

									// log hidden containers bigger than 1 kB
									if (size > 1024) {
										phantomas.addOffender('hiddenContentSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
									}
								}

								// don't run for child nodes as they're hidden as well
								return false;
							}

							// count nodes with inline CSS
							if (node.hasAttribute('style')) {
								phantomas.incr('nodesWithInlineCSS');
								phantomas.addOffender('nodesWithInlineCSS', phantomas.getDOMPath(node) + ' (' + node.getAttribute('style')  + ')');
							}

							break;

						case Node.TEXT_NODE:
							if (whitespacesRegExp.test(node.textContent)) {
								phantomas.incr('whiteSpacesSize', node.textContent.length);
							}
							break;
					}
				});

				phantomas.spyEnabled(false, 'counting iframes and images');

				// count <iframe> tags
				phantomas.setMetric('iframesCount', document.querySelectorAll('iframe').length);

				// <img> nodes without dimensions (one of width / height missing)
				phantomas.setMetric('imagesWithoutDimensions', (function() {
					var imgNodes = document.body && document.body.querySelectorAll('img') || [],
						node,
						imagesWithoutDimensions = 0;

					for (var i=0, len=imgNodes.length; i<len; i++) {
						node = imgNodes[i];
						if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
							phantomas.addOffender('imagesWithoutDimensions', phantomas.getDOMPath(node));
							imagesWithoutDimensions++;
						}
					}

					return imagesWithoutDimensions;
				})());

				phantomas.spyEnabled(true);
			}(window.__phantomas));
		});

		// total length of HTML comments (including <!-- --> brackets)
		phantomas.setMetricFromScope('commentsSize');

		// total length of HTML of hidden elements (i.e. display: none)
		phantomas.setMetricFromScope('hiddenContentSize');

		// total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
		phantomas.setMetricFromScope('whiteSpacesSize');

		// count all tags
		phantomas.setMetricFromScope('DOMelementsCount');
		phantomas.setMetricFromScope('DOMelementMaxDepth');

		// nodes with inlines CSS (style attribute)
		phantomas.setMetricFromScope('nodesWithInlineCSS');
	});
};
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const should = require('should');
const uuid = require('uuid/v1');
const _ = require('lodash');

const initialization = require("./init.js");
const exampleData = require("./exampleData.js");

describe('couchbase test cases', function() {
  let db, countries, CountryModel, CountryModelWithId, StudentModel;

  before(function(done) {
    db = initialization.getDataSource();
    countries = exampleData.countries;
    CountryModel = db.define('CountryModel', {
      gdp: Number,
      countryCode: String,
      name: String,
      population: Number,
      updatedAt: Date
    }, {
      forceId: false
    });
    CountryModelWithId = db.define('CountryModelWithId', {
      id: {type: String, id: true},
      gdp: Number,
      countryCode: String,
      name: String,
      population: Number,
      updatedAt: Date
    });
    StudentModel = db.define('StudentModel', {
      name: {type: String, length: 255},
      age: {type: Number}
    }, {
      forceId: false
    });

    deleteAllModelInstances();
    done();
  });

  function verifyCountryRows(err, m) {
      should.not.exists(err);
      should.exist(m && m.id);
      should.exist(m && m.gdp);
      should.exist(m && m.countryCode);
      should.exist(m && m.name);
      should.exist(m && m.population);
      should.exist(m && m.updatedAt);
      m.gdp.should.be.type('number');
      m.countryCode.should.be.type('string');
      m.name.should.be.type('string');
      m.population.should.be.type('number');
      m.updatedAt.should.be.type('object');
    }

  describe('create document', function() {
    it('create a document and generate an id', function(done) {
      CountryModel.create(countries[0], function(err, res) {
        verifyCountryRows(err, res);
        done();
      });
    });

    it('create a document that has an id defined', function(done) {
      const id = uuid();

      let newCountry = _.omit(countries[0]);
      newCountry.id = id;
      CountryModelWithId.create(newCountry, function(err, res) {
        should.not.exists(err);
        assert.equal(res.id, id);
        verifyCountryRows(err, res);
        done();
      });
    });

    it('create a document that has an id defined but empty', function(done) {
      const id = uuid();

      let newCountry = _.omit(countries[0]);
      CountryModelWithId.create(newCountry, function(err, res) {
        should.not.exists(err);
        should.exist(res && res.id);
        verifyCountryRows(err, res);
        done();
      });
    });
  });

  describe('update document', function() {
    let country, countryId;

    beforeEach(function(done) {
        CountryModelWithId.create(countries[1], function(err, res) {
          country = res;
          countryId = 'CountryModel::' + res.id;
          done();
        })
    });

    it('update a document', function(done) {
      let newCountry = _.omit(countries[2], 'population');
      country.updateAttributes(newCountry, function(err, res) {
        should.not.exists(err);
        should.exist(res && res.id);
        verifyCountryRows(err, res);
        done();
      })
    });

    it('upsert a document', function(done) {
      let newCountry = new CountryModelWithId(countries[2]);
      should.not.exist(newCountry.id);
      newCountry.save(function(err, instance) {
        should.exist(instance.id);
        should.exist(newCountry.id);
        //verifyCountryRows(err, instance);
        done();
      });
    });

    // TODO: find an object by id an update id
    // Person.findOne(function(err, p) {
        //   if (err) return done(err);
        //   p.name = 'Hans';
        //   p.save(function(err) {
        //     if (err) return done(err);
        //     p.name.should.equal('Hans');
        //     Person.findOne(function(err, p) {
        //       if (err) return done(err);
        //       p.name.should.equal('Hans');
        //       done();
        //     });
        //   });
        // });
  });

  describe('find document', function() {
    beforeEach(function(done) {
      CountryModelWithId.destroyAll(done);
    });

    it('find all instances without filter', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            CountryModelWithId.find(function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(2);
              done();
            });
          });
        });
      });
    });

    // TODO: Improve assertions
    it('find one instance with limit and skip', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            StudentModel.find({limit: 1, offset: 0}, function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(1);

              CountryModelWithId.find({limit: 1, offset: 1}, function(err, response) {
                should.not.exist(err);
                response.length.should.be.equal(1);
                done();
              });
            });
          });
        });
      });
    });

    it('retrieve only one field', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            CountryModelWithId.find({fields: ['name', 'population']}, function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(2);
              should.exist(response[0].name);
              should.exist(response[0].population);
              should.not.exist(response[0].id);
              done();
            });
          });
        });
      });
    });

    it('should allow to find using equal', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: 'Ecuador'}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should allow to find using like', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {like: 'E%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support like for no match', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {like: 'M%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should allow to find using nlike', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {nlike: 'E%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should support nlike for no match', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {nlike: 'M%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "and" operator that is satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {and: [
          {name: 'Ecuador'},
          {countryCode: 'EC'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "and" operator that is not satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {and: [
          {name: 'Ecuador'},
          {countryCode: 'CO'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should support "or" operator that is satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {or: [
          {name: 'Ecuador'},
          {countryCode: 'CO'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "or" operator that is not satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {or: [
          {name: 'Ecuador1'},
          {countryCode: 'EC1'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    describe('null vals in different operators', function() {
      let defaultCountry = _.omit(exampleData.countries[0]);

      it('should handle null in inq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {inq: [null, id]}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Ecuador');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in nin operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Peru';
        defaultCountry.countryCode = 'PE';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {nin: [null, uuid()]}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Peru');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in neq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {neq: null}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Ecuador');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in neq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.updatedAt = undefined;

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {and: [
              {id: {nin: [null]}},
              {name: {nin: [null]}},
              {countryCode: {nin: [null]}}
            ]}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            done();
          });
        });
      });

      it('should support where for count', function(done) {
        CountryModel.create({name: 'My Country', countryCode: 'MC'}, function(err, response) {
          CountryModel.count({and: [
            {name: 'My Country'},
            {countryCode: 'MC'},
          ]}, function(err, count) {
            should.not.exist(err);
            count.should.be.equal(1);
            CountryModel.count({and: [
              {name: 'My Country1'},
              {countryCode: 'MC'},
            ]}, function(err, count) {
              should.not.exist(err);
              count.should.be.equal(0);
              CountryModel.count(function(err, count) {
                should.not.exist(err);
                count.should.be.equal(1);
                done();
              });
            });
          });
        });
      });
    });

    describe('findById method', function() {
      let defaultCountry = _.omit(exampleData.countries[1]);

      it('should return one document', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.findById(id, function(err, response) {
            should.not.exist(err);
            response.name.should.equal('Ecuador');
            response.id.should.equal(id);
            done();
          });
        });
      });
    });
  });

  function deleteAllModelInstances() {
    // TODO: Replace when deleteAll will be defined
    CountryModelWithId.deleteAll();
  }

  after(function() {
    //return deleteAllModelInstances();
  })
});
<commit_msg>Test cases for exists method<commit_after>'use strict';

const assert = require('assert');
const should = require('should');
const uuid = require('uuid/v1');
const _ = require('lodash');

const initialization = require("./init.js");
const exampleData = require("./exampleData.js");

describe('couchbase test cases', function() {
  let db, countries, CountryModel, CountryModelWithId, StudentModel;

  before(function(done) {
    db = initialization.getDataSource();
    countries = exampleData.countries;
    CountryModel = db.define('CountryModel', {
      gdp: Number,
      countryCode: String,
      name: String,
      population: Number,
      updatedAt: Date
    }, {
      forceId: false
    });
    CountryModelWithId = db.define('CountryModelWithId', {
      id: {type: String, id: true},
      gdp: Number,
      countryCode: String,
      name: String,
      population: Number,
      updatedAt: Date
    });
    StudentModel = db.define('StudentModel', {
      name: {type: String, length: 255},
      age: {type: Number}
    }, {
      forceId: false
    });

    deleteAllModelInstances();
    done();
  });

  function verifyCountryRows(err, m) {
      should.not.exists(err);
      should.exist(m && m.id);
      should.exist(m && m.gdp);
      should.exist(m && m.countryCode);
      should.exist(m && m.name);
      should.exist(m && m.population);
      should.exist(m && m.updatedAt);
      m.gdp.should.be.type('number');
      m.countryCode.should.be.type('string');
      m.name.should.be.type('string');
      m.population.should.be.type('number');
      m.updatedAt.should.be.type('object');
    }

  describe('create document', function() {
    it('create a document and generate an id', function(done) {
      CountryModel.create(countries[0], function(err, res) {
        verifyCountryRows(err, res);
        done();
      });
    });

    it('create a document that has an id defined', function(done) {
      const id = uuid();

      let newCountry = _.omit(countries[0]);
      newCountry.id = id;
      CountryModelWithId.create(newCountry, function(err, res) {
        should.not.exists(err);
        assert.equal(res.id, id);
        verifyCountryRows(err, res);
        done();
      });
    });

    it('create a document that has an id defined but empty', function(done) {
      const id = uuid();

      let newCountry = _.omit(countries[0]);
      CountryModelWithId.create(newCountry, function(err, res) {
        should.not.exists(err);
        should.exist(res && res.id);
        verifyCountryRows(err, res);
        done();
      });
    });
  });

  describe('update document', function() {
    let country, countryId;

    beforeEach(function(done) {
        CountryModelWithId.create(countries[1], function(err, res) {
          country = res;
          countryId = 'CountryModel::' + res.id;
          done();
        })
    });

    it('update a document', function(done) {
      let newCountry = _.omit(countries[2], 'population');
      country.updateAttributes(newCountry, function(err, res) {
        should.not.exists(err);
        should.exist(res && res.id);
        verifyCountryRows(err, res);
        done();
      })
    });

    it('upsert a document', function(done) {
      let newCountry = new CountryModelWithId(countries[2]);
      should.not.exist(newCountry.id);
      newCountry.save(function(err, instance) {
        should.exist(instance.id);
        should.exist(newCountry.id);
        //verifyCountryRows(err, instance);
        done();
      });
    });

    // TODO: find an object by id an update id
    // Person.findOne(function(err, p) {
        //   if (err) return done(err);
        //   p.name = 'Hans';
        //   p.save(function(err) {
        //     if (err) return done(err);
        //     p.name.should.equal('Hans');
        //     Person.findOne(function(err, p) {
        //       if (err) return done(err);
        //       p.name.should.equal('Hans');
        //       done();
        //     });
        //   });
        // });
  });

  describe('find document', function() {
    beforeEach(function(done) {
      CountryModelWithId.destroyAll(done);
    });

    it('find all instances without filter', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            CountryModelWithId.find(function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(2);
              done();
            });
          });
        });
      });
    });

    // TODO: Improve assertions
    it('find one instance with limit and skip', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            StudentModel.find({limit: 1, offset: 0}, function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(1);

              CountryModelWithId.find({limit: 1, offset: 1}, function(err, response) {
                should.not.exist(err);
                response.length.should.be.equal(1);
                done();
              });
            });
          });
        });
      });
    });

    it('retrieve only one field', function(done) {
      CountryModelWithId.create(countries[0], function(err, country) {
        CountryModelWithId.create(countries[1], function(err, country) {
          StudentModel.create({name: 'Juan Almeida', age: 30}, function(err, person) {
            CountryModelWithId.find({fields: ['name', 'population']}, function(err, response) {
              should.not.exist(err);
              response.length.should.be.equal(2);
              should.exist(response[0].name);
              should.exist(response[0].population);
              should.not.exist(response[0].id);
              done();
            });
          });
        });
      });
    });

    it('should allow to find using equal', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: 'Ecuador'}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should allow to find using like', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {like: 'E%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support like for no match', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {like: 'M%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should allow to find using nlike', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {nlike: 'E%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should support nlike for no match', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {name: {nlike: 'M%or'}}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "and" operator that is satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {and: [
          {name: 'Ecuador'},
          {countryCode: 'EC'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "and" operator that is not satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {and: [
          {name: 'Ecuador'},
          {countryCode: 'CO'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    it('should support "or" operator that is satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {or: [
          {name: 'Ecuador'},
          {countryCode: 'CO'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',1);
          done();
        });
      });
    });

    it('should support "or" operator that is not satisfied', function(done) {
      CountryModel.create({name: 'Ecuador', countryCode: 'EC'}, function(err, country) {
        CountryModel.find({where: {or: [
          {name: 'Ecuador1'},
          {countryCode: 'EC1'}
        ]}}, function(err, response) {
          should.not.exists(err);
          response.should.have.property('length',0);
          done();
        });
      });
    });

    describe('null vals in different operators', function() {
      let defaultCountry = _.omit(exampleData.countries[0]);

      it('should handle null in inq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {inq: [null, id]}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Ecuador');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in nin operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Peru';
        defaultCountry.countryCode = 'PE';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {nin: [null, uuid()]}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Peru');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in neq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {id: {neq: null}}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            response[0].name.should.equal('Ecuador');
            response[0].id.should.equal(id);
            done();
          });
        });
      });

      it('should handle null in neq operator', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.updatedAt = undefined;

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.find({where: {and: [
              {id: {nin: [null]}},
              {name: {nin: [null]}},
              {countryCode: {nin: [null]}}
            ]}}, function(err, response) {
            should.not.exist(err);
            response.length.should.equal(1);
            done();
          });
        });
      });

      it('should support where for count', function(done) {
        CountryModel.create({name: 'My Country', countryCode: 'MC'}, function(err, response) {
          CountryModel.count({and: [
            {name: 'My Country'},
            {countryCode: 'MC'},
          ]}, function(err, count) {
            should.not.exist(err);
            count.should.be.equal(1);
            CountryModel.count({and: [
              {name: 'My Country1'},
              {countryCode: 'MC'},
            ]}, function(err, count) {
              should.not.exist(err);
              count.should.be.equal(0);
              CountryModel.count(function(err, count) {
                should.not.exist(err);
                count.should.be.equal(1);
                done();
              });
            });
          });
        });
      });
    });

    describe('findById method', function() {
      let defaultCountry = _.omit(exampleData.countries[1]);

      it('should return one document', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Ecuador';
        defaultCountry.countryCode = 'EC';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.findById(id, function(err, response) {
            should.not.exist(err);
            response.name.should.equal('Ecuador');
            response.id.should.equal(id);
            done();
          });
        });
      });
    });

    describe('exists method', function() {
      let defaultCountry = _.omit(exampleData.countries[1]);

      it('should return true because document exists', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Peru';
        defaultCountry.countryCode = 'PE';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.exists(id, function(err, response) {
            should.not.exist(err);
            response.should.be.true;
            done();
          });
        });
      });

      it('should return false because document does not exists', function(done) {
        let id = uuid();

        defaultCountry.id = id;
        defaultCountry.name = 'Peru';
        defaultCountry.countryCode = 'PE';

        CountryModelWithId.create(defaultCountry, function(err, response) {
          should.not.exist(err);
          response.id.should.equal(defaultCountry.id);

          CountryModelWithId.exists(uuid(), function(err, response) {
            should.not.exist(err);
            response.should.be.false;
            done();
          });
        });
      });
    });
  });

  function deleteAllModelInstances() {
    // TODO: Replace when deleteAll will be defined
    CountryModelWithId.deleteAll();
  }

  after(function() {
    //return deleteAllModelInstances();
  })
});
<|endoftext|>