<commit_before>import { fireShuffleTasks } from 'src/model/ModelBase';
import { REFERENCE } from 'config/types';
import { rebindMatch } from 'shared/rebind';
import { isArray, isString } from 'utils/is';
import { escapeKey } from 'shared/keypaths';
import ExpressionProxy from './ExpressionProxy';
import resolveReference from './resolveReference';
import resolve from './resolve';

import LinkModel, { Missing } from 'src/model/LinkModel';

export default class ReferenceExpressionProxy extends LinkModel {
  constructor(fragment, template) {
    super(null, null, null, '@undefined');
    this.root = fragment.ractive.viewmodel;
    this.template = template;
    this.rootLink = true;
    this.template = template;
    this.fragment = fragment;

    this.rebound();
  }

  getKeypath() {
    return this.model ? this.model.getKeypath() : '@undefined';
  }

  rebound() {
    const fragment = this.fragment;
    const template = this.template;

    let base = (this.base = resolve(fragment, template));
    let idx;

    if (this.proxy) {
      teardown(this);
    }

    const proxy = (this.proxy = {
      rebind: (next, previous) => {
        if (previous === base) {
          next = rebindMatch(template, next, previous);
          if (next !== base) {
            this.base = base = next;
          }
        } else if (~(idx = members.indexOf(previous))) {
          next = rebindMatch(template.m[idx].n, next, previous);
          if (next !== members[idx]) {
            members.splice(idx, 1, next || Missing);
          }
        }

        if (next !== previous) previous.unregister(proxy);
        if (next) next.addShuffleTask(() => next.register(proxy));
      },
      handleChange: () => {
        pathChanged();
      }
    });

    base.register(proxy);

    const members = (this.members = template.m.map(tpl => {
      if (isString(tpl)) {
        return { get: () => tpl };
      }

      let model;

      if (tpl.t === REFERENCE) {
        model = resolveReference(fragment, tpl.n);
        model.register(proxy);

        return model;
      }

      model = new ExpressionProxy(fragment, tpl);
      model.register(proxy);
      return model;
    }));

    const pathChanged = () => {
      const model = base.joinAll(
        members.reduce((list, m) => {
          const k = m.get();
          if (isArray(k)) return list.concat(k);
          else list.push(escapeKey(String(k)));
          return list;
        }, [])
      );

      if (model !== this.model) {
        this.model = model;
        this.relinking(model);
        fireShuffleTasks();
        refreshPathDeps(this);
      }
    };

    pathChanged();
  }

  teardown() {
    teardown(this);
    super.teardown();
  }
}

function teardown(proxy) {
  if (proxy.base) proxy.base.unregister(proxy.proxy);
  if (proxy.models) {
    proxy.models.forEach(m => {
      if (m.unregister) m.unregister(proxy);
    });
  }
}

function refreshPathDeps(proxy) {
  let len = proxy.deps.length;
  let i, v;

  for (i = 0; i < len; i++) {
    v = proxy.deps[i];
    if (v.pathChanged) v.pathChanged();
    if (v.fragment && v.fragment.pathModel) v.fragment.pathModel.applyValue(proxy.getKeypath());
  }

  len = proxy.children.length;
  for (i = 0; i < len; i++) {
    refreshPathDeps(proxy.children[i]);
  }
}

const eproto = ExpressionProxy.prototype;
const proto = ReferenceExpressionProxy.prototype;

proto.unreference = eproto.unreference;
proto.unregister = eproto.unregister;
proto.unregisterLink = eproto.unregisterLink;
<commit_msg>don't reregister ref expr proxy during rebind if the target didn't change - fixes #3232<commit_after>import { fireShuffleTasks } from 'src/model/ModelBase';
import { REFERENCE } from 'config/types';
import { rebindMatch } from 'shared/rebind';
import { isArray, isString } from 'utils/is';
import { escapeKey } from 'shared/keypaths';
import ExpressionProxy from './ExpressionProxy';
import resolveReference from './resolveReference';
import resolve from './resolve';

import LinkModel, { Missing } from 'src/model/LinkModel';

export default class ReferenceExpressionProxy extends LinkModel {
  constructor(fragment, template) {
    super(null, null, null, '@undefined');
    this.root = fragment.ractive.viewmodel;
    this.template = template;
    this.rootLink = true;
    this.template = template;
    this.fragment = fragment;

    this.rebound();
  }

  getKeypath() {
    return this.model ? this.model.getKeypath() : '@undefined';
  }

  rebound() {
    const fragment = this.fragment;
    const template = this.template;

    let base = (this.base = resolve(fragment, template));
    let idx;

    if (this.proxy) {
      teardown(this);
    }

    const proxy = (this.proxy = {
      rebind: (next, previous) => {
        if (previous === base) {
          next = rebindMatch(template, next, previous);
          if (next !== base) {
            this.base = base = next;
          }
        } else if (~(idx = members.indexOf(previous))) {
          next = rebindMatch(template.m[idx].n, next, previous);
          if (next !== members[idx]) {
            members.splice(idx, 1, next || Missing);
          }
        }

        if (next !== previous) {
          previous.unregister(proxy);
          if (next) next.addShuffleTask(() => next.register(proxy));
        }
      },
      handleChange: () => {
        pathChanged();
      }
    });

    base.register(proxy);

    const members = (this.members = template.m.map(tpl => {
      if (isString(tpl)) {
        return { get: () => tpl };
      }

      let model;

      if (tpl.t === REFERENCE) {
        model = resolveReference(fragment, tpl.n);
        model.register(proxy);

        return model;
      }

      model = new ExpressionProxy(fragment, tpl);
      model.register(proxy);
      return model;
    }));

    const pathChanged = () => {
      const model = base.joinAll(
        members.reduce((list, m) => {
          const k = m.get();
          if (isArray(k)) return list.concat(k);
          else list.push(escapeKey(String(k)));
          return list;
        }, [])
      );

      if (model !== this.model) {
        this.model = model;
        this.relinking(model);
        fireShuffleTasks();
        refreshPathDeps(this);
      }
    };

    pathChanged();
  }

  teardown() {
    teardown(this);
    super.teardown();
  }

  unreference() {
    super.unreference();
    if (!this.deps.length && !this.refs) this.teardown();
  }

  unregister(dep) {
    super.unregister(dep);
    if (!this.deps.length && !this.refs) this.teardown();
  }
}

function teardown(proxy) {
  if (proxy.base) proxy.base.unregister(proxy.proxy);
  if (proxy.models) {
    proxy.models.forEach(m => {
      if (m.unregister) m.unregister(proxy);
    });
  }
}

function refreshPathDeps(proxy) {
  let len = proxy.deps.length;
  let i, v;

  for (i = 0; i < len; i++) {
    v = proxy.deps[i];
    if (v.pathChanged) v.pathChanged();
    if (v.fragment && v.fragment.pathModel) v.fragment.pathModel.applyValue(proxy.getKeypath());
  }

  len = proxy.children.length;
  for (i = 0; i < len; i++) {
    refreshPathDeps(proxy.children[i]);
  }
}

const eproto = ExpressionProxy.prototype;
const proto = ReferenceExpressionProxy.prototype;

proto.unreference = eproto.unreference;
proto.unregister = eproto.unregister;
proto.unregisterLink = eproto.unregisterLink;
<|endoftext|>


<commit_before>'use strict';
const _ = require('underscore');
const ArangoError = require('org/arangodb').ArangoError;
const errors = require('org/arangodb').errors;
const internal = require('internal');
const assert = require('assert');
const Module = require('module');
const path = require('path');
const fs = require('fs');
const parameterTypes = require('org/arangodb/foxx/manager-utils').parameterTypes;
const getReadableName = require('org/arangodb/foxx/manager-utils').getReadableName;
const getExports = require('org/arangodb/foxx').getExports;

const APP_PATH = internal.appPath ? path.resolve(internal.appPath) : undefined;
const STARTUP_PATH = internal.startupPath ? path.resolve(internal.startupPath) : undefined;
const DEV_APP_PATH = internal.devAppPath ? path.resolve(internal.devAppPath) : undefined;

class AppContext {
  constructor(service) {
    this.basePath = path.resolve(service.root, service.path);
    this.comments = [];
    Object.defineProperty(this, '_service', {
      get() {
        return service;
      }
    });
  }

  foxxFilename(filename) {
    return fs.safeJoin(this._prefix, filename);
  }

  path(name) {
    return path.join(this._prefix, name);
  }

  collectionName(name) {
    let fqn = (
      this.collectionPrefix
      + name.replace(/[^a-z0-9]/ig, '_').replace(/(^_+|_+$)/g, '').substr(0, 64)
    );
    if (!fqn.length) {
      throw new Error(`Cannot derive collection name from "${name}"`);
    }
  }

  collection(name) {
    return internal.db._collection(this.collectionName(name));
  }

  get _prefix() {
    return this.basePath;
  }

  get baseUrl() {
    return `/_db/${encodeURIComponent(internal.db._name())}/${this._service.mount.slice(1)}`;
  }

  get collectionPrefix() {
    return this._service.collectionPrefix;
  }

  get mount() {
    return this._service.mount;
  }

  get name() {
    return this._service.name;
  }

  get version() {
    return this._service.version;
  }

  get manifest() {
    return this._service.manifest;
  }

  get isDevelopment() {
    return this._service.isDevelopment;
  }

  get isProduction() {
    return !this.isDevelopment;
  }

  get options() {
    return this._service.options;
  }

  get configuration() {
    return this._service.configuration;
  }

  get dependencies() {
    return this._service.dependencies;
  }
}

Object.defineProperties(AppContext.prototype, {
  comment: {
    value: function (str) {
      this.comments.push(str);
    }
  },
  clearComments: {
    value: function () {
      this.comments.splice(0, this.comments.length);
    }
  }
});

function createConfiguration(definitions) {
  const config = {};
  Object.keys(definitions).forEach(function (name) {
    const def = definitions[name];
    if (def.default !== undefined) {
      config[name] = def.default;
    }
  });
  return config;
}

function createDependencies(definitions, options) {
  const deps = {};
  Object.keys(definitions).forEach(function (name) {
    Object.defineProperty(deps, name, {
      configurable: true,
      enumerable: true,
      get() {
        const mount = options[name];
        return mount ? getExports(mount) : undefined;
      }
    });
  });
  return deps;
}

class FoxxService {
  constructor(data) {
    assert(data, 'no arguments');
    assert(data.mount, 'mount path required');
    assert(data.path, `local path required for app "${data.mount}"`);
    assert(data.manifest, `called without manifest for app "${data.mount}"`);

    // mount paths always start with a slash
    this.mount = data.mount;
    this.path = data.path;
    this.isDevelopment = data.isDevelopment || false;

    this.manifest = data.manifest;
    if (!this.manifest.dependencies) {
      this.manifest.dependencies = {};
    }
    if (!this.manifest.configuration) {
      this.manifest.configuration = {};
    }

    this.options = data.options;
    if (!this.options.configuration) {
      this.options.configuration = {};
    }
    if (!this.options.dependencies) {
      this.options.dependencies = {};
    }

    this.configuration = createConfiguration(this.manifest.configuration);
    this.dependencies = createDependencies(this.manifest.dependencies, this.options.dependencies);
    let warnings = this.applyConfiguration(this.options.configuration);
    if (warnings.length) {
      console.warnLines(
        `Stored configuration for app "${data.mount}" has errors:\n${warnings.join('\n  ')}`
      );
    }
    // don't need to apply deps from options -- they work automatically

    if (this.manifest.thumbnail) {
      let thumb = path.resolve(this.root, this.path, this.manifest.thumbnail);
      try {
        this.thumbnail = fs.read64(thumb);
      } catch (e) {
        console.warnLines(
          `Cannot read thumbnail "${thumb}" for app "${data.mount}": ${e.stack}`
        );
      }
    } else {
      this.thumbnail = null;
    }

    let lib = this.manifest.lib || '.';
    this.main = new Module(`foxx:${data.mount}`);
    this.main.filename = path.resolve(this.root, this.path, lib, '.foxx');
    this.main.context.applicationContext = new AppContext(this);
    this.main.context.console = require('org/arangodb/foxx/console')(this.mount);
  }

  applyConfiguration(config) {
    const definitions = this.manifest.configuration;
    const warnings = [];

    _.each(config, function (rawValue, name) {
      const def = definitions[name];
      if (!def) {
        warnings.push(`Unexpected option "${name}"`);
        return;
      }

      if (def.required === false && (rawValue === undefined || rawValue === null)) {
        delete this.options.configuration[name];
        this.configuration[name] = def.default;
        return;
      }

      const validate = parameterTypes[def.type];
      let parsedValue = rawValue;
      let warning;

      if (validate.isJoi) {
        let result = validate.required().validate(rawValue);
        if (result.error) {
          warning = result.error.message.replace(/^"value"/, `"${name}"`);
        } else {
          parsedValue = result.value;
        }
      } else {
        try {
          parsedValue = validate(rawValue);
        } catch (e) {
          warning = `"${name}": ${e.message}`;
        }
      }

      if (warning) {
        warnings.push(warning);
      } else {
        this.options.configuration[name] = rawValue;
        this.configuration[name] = parsedValue;
      }
    }, this);

    return warnings;
  }

  applyDependencies(deps) {
    var definitions = this.manifest.dependencies;
    var warnings = [];

    _.each(deps, function (mount, name) {
      const dfn = definitions[name];
      if (!dfn) {
        warnings.push(`Unexpected dependency "${name}"`);
      } else {
        this.options.dependencies[name] = mount;
      }
    }, this);

    return warnings;
  }

  _PRINT(context) {
    context.output += `[FoxxService "${this.name}" (${this.version}) on ${this.mount}]`;
  }

  toJSON() {
    const result = {
      name: this.name,
      version: this.version,
      manifest: this.manifest,
      path: this.path,
      options: this.options,
      mount: this.mount,
      isSystem: this.isSystem,
      isDevelopment: this.isDevelopment
    };
    if (this.error) {
      result.error = this.error;
    }
    if (this.manifest.author) {
      result.author = this.manifest.author;
    }
    if (this.manifest.description) {
      result.description = this.manifest.description;
    }
    if (this.thumbnail) {
      result.thumbnail = this.thumbnail;
    }
    return result;
  }

  simpleJSON() {
    return {
      name: this.name,
      version: this.version,
      mount: this.mount
    };
  }

  development(isDevelopment) {
    this.isDevelopment = isDevelopment;
  }

  getConfiguration(simple) {
    var config = {};
    var definitions = this.manifest.configuration;
    var options = this.options.configuration;
    _.each(definitions, function (dfn, name) {
      var value = options[name] === undefined ? dfn.default : options[name];
      config[name] = simple ? value : _.extend(_.clone(dfn), {
        title: getReadableName(name),
        current: value
      });
    });
    return config;
  }

  getDependencies(simple) {
    var deps = {};
    var definitions = this.manifest.dependencies;
    var options = this.options.dependencies;
    _.each(definitions, function (dfn, name) {
      deps[name] = simple ? options[name] : {
        definition: dfn,
        title: getReadableName(name),
        current: options[name]
      };
    });
    return deps;
  }

  needsConfiguration() {
    var config = this.getConfiguration();
    var deps = this.getDependencies();
    return _.any(config, function (cfg) {
      return cfg.current === undefined && cfg.required !== false;
    }) || _.any(deps, function (dep) {
      return dep.current === undefined && dep.definition.required !== false;
    });
  }

  run(filename, options) {
    options = options || {};
    filename = path.resolve(this.main.context.__dirname, filename);

    var module = new Module(filename, this.main);
    module.context.applicationContext = _.extend(
      new AppContext(this.main.context.applicationContext._service),
      this.main.context.applicationContext,
      options.appContext
    );

    if (options.preprocess) {
      module.preprocess = options.preprocess;
    }

    if (options.context) {
      Object.keys(options.context).forEach(function (key) {
        module.context[key] = options.context[key];
      });
    }

    try {
      module.load(filename);
      return module.exports;
    } catch(e) {
      if (e instanceof ArangoError) {
        throw e;
      }
      var err = new ArangoError({
        errorNum: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.code,
        errorMessage: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.message
        + '\nFile: ' + filename
      });
      err.stack = e.stack;
      err.cause = e;
      throw err;
    }
  }

  get exports() {
    return this.main.exports;
  }

  get name() {
    return this.manifest.name;
  }

  get version() {
    return this.manifest.version;
  }

  get isSystem() {
    return this.mount.charAt(1) === '_';
  }

  get root() {
    if (this.isSystem) {
      return FoxxService._systemAppPath;
    }
    return FoxxService._appPath;
  }

  get collectionPrefix() {
    if (this.isSystem) {
      return '';
    }
    return this.mount.substr(1).replace(/[-.:/]/g, '_') + '_';
  }

  static get _startupPath() {
    return STARTUP_PATH;
  }

  static get _appPath() {
    return APP_PATH ? (
      path.join(APP_PATH, '_db', internal.db._name())
    ) : undefined;
  }

  static get _systemAppPath() {
    return APP_PATH ? (
      path.join(STARTUP_PATH, 'apps', 'system')
    ) : undefined;
  }

  static get _oldAppPath() {
    return APP_PATH ? (
      path.join(APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }

  static get _devAppPath() {
    return DEV_APP_PATH ? (
      path.join(DEV_APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }
}

module.exports = FoxxService;
<commit_msg>Fixed cyclic import<commit_after>'use strict';
const _ = require('underscore');
const ArangoError = require('org/arangodb').ArangoError;
const errors = require('org/arangodb').errors;
const internal = require('internal');
const assert = require('assert');
const Module = require('module');
const path = require('path');
const fs = require('fs');
const parameterTypes = require('org/arangodb/foxx/manager-utils').parameterTypes;
const getReadableName = require('org/arangodb/foxx/manager-utils').getReadableName;

const APP_PATH = internal.appPath ? path.resolve(internal.appPath) : undefined;
const STARTUP_PATH = internal.startupPath ? path.resolve(internal.startupPath) : undefined;
const DEV_APP_PATH = internal.devAppPath ? path.resolve(internal.devAppPath) : undefined;

class AppContext {
  constructor(service) {
    this.basePath = path.resolve(service.root, service.path);
    this.comments = [];
    Object.defineProperty(this, '_service', {
      get() {
        return service;
      }
    });
  }

  foxxFilename(filename) {
    return fs.safeJoin(this._prefix, filename);
  }

  path(name) {
    return path.join(this._prefix, name);
  }

  collectionName(name) {
    let fqn = (
      this.collectionPrefix
      + name.replace(/[^a-z0-9]/ig, '_').replace(/(^_+|_+$)/g, '').substr(0, 64)
    );
    if (!fqn.length) {
      throw new Error(`Cannot derive collection name from "${name}"`);
    }
  }

  collection(name) {
    return internal.db._collection(this.collectionName(name));
  }

  get _prefix() {
    return this.basePath;
  }

  get baseUrl() {
    return `/_db/${encodeURIComponent(internal.db._name())}/${this._service.mount.slice(1)}`;
  }

  get collectionPrefix() {
    return this._service.collectionPrefix;
  }

  get mount() {
    return this._service.mount;
  }

  get name() {
    return this._service.name;
  }

  get version() {
    return this._service.version;
  }

  get manifest() {
    return this._service.manifest;
  }

  get isDevelopment() {
    return this._service.isDevelopment;
  }

  get isProduction() {
    return !this.isDevelopment;
  }

  get options() {
    return this._service.options;
  }

  get configuration() {
    return this._service.configuration;
  }

  get dependencies() {
    return this._service.dependencies;
  }
}

Object.defineProperties(AppContext.prototype, {
  comment: {
    value: function (str) {
      this.comments.push(str);
    }
  },
  clearComments: {
    value: function () {
      this.comments.splice(0, this.comments.length);
    }
  }
});

function createConfiguration(definitions) {
  const config = {};
  Object.keys(definitions).forEach(function (name) {
    const def = definitions[name];
    if (def.default !== undefined) {
      config[name] = def.default;
    }
  });
  return config;
}

function createDependencies(definitions, options) {
  const deps = {};
  Object.keys(definitions).forEach(function (name) {
    Object.defineProperty(deps, name, {
      configurable: true,
      enumerable: true,
      get() {
        const mount = options[name];
        return mount ? require('org/arangodb/foxx').getExports(mount) : null;
      }
    });
  });
  return deps;
}

class FoxxService {
  constructor(data) {
    assert(data, 'no arguments');
    assert(data.mount, 'mount path required');
    assert(data.path, `local path required for app "${data.mount}"`);
    assert(data.manifest, `called without manifest for app "${data.mount}"`);

    // mount paths always start with a slash
    this.mount = data.mount;
    this.path = data.path;
    this.isDevelopment = data.isDevelopment || false;

    this.manifest = data.manifest;
    if (!this.manifest.dependencies) {
      this.manifest.dependencies = {};
    }
    if (!this.manifest.configuration) {
      this.manifest.configuration = {};
    }

    this.options = data.options;
    if (!this.options.configuration) {
      this.options.configuration = {};
    }
    if (!this.options.dependencies) {
      this.options.dependencies = {};
    }

    this.configuration = createConfiguration(this.manifest.configuration);
    this.dependencies = createDependencies(this.manifest.dependencies, this.options.dependencies);
    let warnings = this.applyConfiguration(this.options.configuration);
    if (warnings.length) {
      console.warnLines(
        `Stored configuration for app "${data.mount}" has errors:\n${warnings.join('\n  ')}`
      );
    }
    // don't need to apply deps from options -- they work automatically

    if (this.manifest.thumbnail) {
      let thumb = path.resolve(this.root, this.path, this.manifest.thumbnail);
      try {
        this.thumbnail = fs.read64(thumb);
      } catch (e) {
        console.warnLines(
          `Cannot read thumbnail "${thumb}" for app "${data.mount}": ${e.stack}`
        );
      }
    } else {
      this.thumbnail = null;
    }

    let lib = this.manifest.lib || '.';
    this.main = new Module(`foxx:${data.mount}`);
    this.main.filename = path.resolve(this.root, this.path, lib, '.foxx');
    this.main.context.applicationContext = new AppContext(this);
    this.main.context.console = require('org/arangodb/foxx/console')(this.mount);
  }

  applyConfiguration(config) {
    const definitions = this.manifest.configuration;
    const warnings = [];

    _.each(config, function (rawValue, name) {
      const def = definitions[name];
      if (!def) {
        warnings.push(`Unexpected option "${name}"`);
        return;
      }

      if (def.required === false && (rawValue === undefined || rawValue === null)) {
        delete this.options.configuration[name];
        this.configuration[name] = def.default;
        return;
      }

      const validate = parameterTypes[def.type];
      let parsedValue = rawValue;
      let warning;

      if (validate.isJoi) {
        let result = validate.required().validate(rawValue);
        if (result.error) {
          warning = result.error.message.replace(/^"value"/, `"${name}"`);
        } else {
          parsedValue = result.value;
        }
      } else {
        try {
          parsedValue = validate(rawValue);
        } catch (e) {
          warning = `"${name}": ${e.message}`;
        }
      }

      if (warning) {
        warnings.push(warning);
      } else {
        this.options.configuration[name] = rawValue;
        this.configuration[name] = parsedValue;
      }
    }, this);

    return warnings;
  }

  applyDependencies(deps) {
    var definitions = this.manifest.dependencies;
    var warnings = [];

    _.each(deps, function (mount, name) {
      const dfn = definitions[name];
      if (!dfn) {
        warnings.push(`Unexpected dependency "${name}"`);
      } else {
        this.options.dependencies[name] = mount;
      }
    }, this);

    return warnings;
  }

  _PRINT(context) {
    context.output += `[FoxxService "${this.name}" (${this.version}) on ${this.mount}]`;
  }

  toJSON() {
    const result = {
      name: this.name,
      version: this.version,
      manifest: this.manifest,
      path: this.path,
      options: this.options,
      mount: this.mount,
      isSystem: this.isSystem,
      isDevelopment: this.isDevelopment
    };
    if (this.error) {
      result.error = this.error;
    }
    if (this.manifest.author) {
      result.author = this.manifest.author;
    }
    if (this.manifest.description) {
      result.description = this.manifest.description;
    }
    if (this.thumbnail) {
      result.thumbnail = this.thumbnail;
    }
    return result;
  }

  simpleJSON() {
    return {
      name: this.name,
      version: this.version,
      mount: this.mount
    };
  }

  development(isDevelopment) {
    this.isDevelopment = isDevelopment;
  }

  getConfiguration(simple) {
    var config = {};
    var definitions = this.manifest.configuration;
    var options = this.options.configuration;
    _.each(definitions, function (dfn, name) {
      var value = options[name] === undefined ? dfn.default : options[name];
      config[name] = simple ? value : _.extend(_.clone(dfn), {
        title: getReadableName(name),
        current: value
      });
    });
    return config;
  }

  getDependencies(simple) {
    var deps = {};
    var definitions = this.manifest.dependencies;
    var options = this.options.dependencies;
    _.each(definitions, function (dfn, name) {
      deps[name] = simple ? options[name] : {
        definition: dfn,
        title: getReadableName(name),
        current: options[name]
      };
    });
    return deps;
  }

  needsConfiguration() {
    var config = this.getConfiguration();
    var deps = this.getDependencies();
    return _.any(config, function (cfg) {
      return cfg.current === undefined && cfg.required !== false;
    }) || _.any(deps, function (dep) {
      return dep.current === undefined && dep.definition.required !== false;
    });
  }

  run(filename, options) {
    options = options || {};
    filename = path.resolve(this.main.context.__dirname, filename);

    var module = new Module(filename, this.main);
    module.context.applicationContext = _.extend(
      new AppContext(this.main.context.applicationContext._service),
      this.main.context.applicationContext,
      options.appContext
    );

    if (options.preprocess) {
      module.preprocess = options.preprocess;
    }

    if (options.context) {
      Object.keys(options.context).forEach(function (key) {
        module.context[key] = options.context[key];
      });
    }

    try {
      module.load(filename);
      return module.exports;
    } catch(e) {
      if (e instanceof ArangoError) {
        throw e;
      }
      var err = new ArangoError({
        errorNum: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.code,
        errorMessage: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.message
        + '\nFile: ' + filename
      });
      err.stack = e.stack;
      err.cause = e;
      throw err;
    }
  }

  get exports() {
    return this.main.exports;
  }

  get name() {
    return this.manifest.name;
  }

  get version() {
    return this.manifest.version;
  }

  get isSystem() {
    return this.mount.charAt(1) === '_';
  }

  get root() {
    if (this.isSystem) {
      return FoxxService._systemAppPath;
    }
    return FoxxService._appPath;
  }

  get collectionPrefix() {
    if (this.isSystem) {
      return '';
    }
    return this.mount.substr(1).replace(/[-.:/]/g, '_') + '_';
  }

  static get _startupPath() {
    return STARTUP_PATH;
  }

  static get _appPath() {
    return APP_PATH ? (
      path.join(APP_PATH, '_db', internal.db._name())
    ) : undefined;
  }

  static get _systemAppPath() {
    return APP_PATH ? (
      path.join(STARTUP_PATH, 'apps', 'system')
    ) : undefined;
  }

  static get _oldAppPath() {
    return APP_PATH ? (
      path.join(APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }

  static get _devAppPath() {
    return DEV_APP_PATH ? (
      path.join(DEV_APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }
}

module.exports = FoxxService;
<|endoftext|>


<commit_before>Ext.define('devilry_subjectadmin.view.dashboard.AllActiveWhereIsAdminList' ,{
    extend: 'Ext.Component',
    alias: 'widget.allactivewhereisadminlist',
    cls: 'devilry_subjectadmin_allactivewhereisadminlist bootstrap',
    tpl: [
        '<tpl if="loadingtext">',
            '
{loadingtext}
',
        '<tpl else>',
            '
',
                interpolate(gettext('Active %(subjects_term)s'), {
                    subjects_term: gettext('subjects')
                }, true),
            '
',
            '
',
                interpolate(gettext('These are your %(subjects_term)s with an active %(period_term)s. Choose a %(period_term)s to create new %(assignments_term)s, manage existing %(assignments_term)s and much more.'), {
                    subjects_term: gettext('subjects'),
                    period_term: gettext('period'),
                    assignments_term: gettext('assignments'),
                    assignment_term: gettext('assignment')
                }, true),
            '
',
            '
',
            '<tpl for="subjects">',
                //'
',
                    //'{[Ext.String.ellipsis(values.data.long_name, 40)]}',
                //'
',
                '<tpl for="data.periods">',
                    '<tpl exec="values.subject = parent.data;"></tpl>',
                    '
',
                        '<tpl if="can_administer">',
                            '
',
                                '{subject.short_name} - ',
                                '{[Ext.String.ellipsis(values.long_name, 40)]}',
                            '
',
                        '<tpl else>',
                            '<tpl for="assignments">',
                                '

',
                                    '{parent.subject.short_name} - ',
                                    '{parent.short_name} - ',
                                    '{[Ext.String.ellipsis(values.long_name, 40)]}',
                                '

',
                            '</tpl>',
                        '</tpl>',
                    '
',
                '</tpl>',
                '
',
            '</tpl>',
            '
',
        '</tpl>'
    ],

    data: {
        loadingtext: gettext('Loading') + ' ...'
    }
});
<commit_msg>devilry_subjectadmin...AllActiveWhereIsAdminList: Periods instead of subjects.<commit_after>Ext.define('devilry_subjectadmin.view.dashboard.AllActiveWhereIsAdminList' ,{
    extend: 'Ext.Component',
    alias: 'widget.allactivewhereisadminlist',
    cls: 'devilry_subjectadmin_allactivewhereisadminlist bootstrap',
    tpl: [
        '<tpl if="loadingtext">',
            '
{loadingtext}
',
        '<tpl else>',
            '
',
                interpolate(gettext('Active %(periods_term)s'), {
                    periods_term: gettext('periods')
                }, true),
            '
',
            '
',
                interpolate(gettext('These are your %(subjects_term)s with an active %(period_term)s. Choose a %(period_term)s to create new %(assignments_term)s, manage existing %(assignments_term)s and much more.'), {
                    subjects_term: gettext('subjects'),
                    period_term: gettext('period'),
                    assignments_term: gettext('assignments'),
                    assignment_term: gettext('assignment')
                }, true),
            '
',
            '
',
            '<tpl for="subjects">',
                //'
',
                    //'{[Ext.String.ellipsis(values.data.long_name, 40)]}',
                //'
',
                '<tpl for="data.periods">',
                    '<tpl exec="values.subject = parent.data;"></tpl>',
                    '
',
                        '<tpl if="can_administer">',
                            '
',
                                '{subject.short_name} - ',
                                '{[Ext.String.ellipsis(values.long_name, 40)]}',
                            '
',
                        '<tpl else>',
                            '<tpl for="assignments">',
                                '

',
                                    '{parent.subject.short_name} - ',
                                    '{parent.short_name} - ',
                                    '{[Ext.String.ellipsis(values.long_name, 40)]}',
                                '

',
                            '</tpl>',
                        '</tpl>',
                    '
',
                '</tpl>',
                '
',
            '</tpl>',
            '
',
        '</tpl>'
    ],

    data: {
        loadingtext: gettext('Loading') + ' ...'
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import Navbar from '../component/navbar';
import {getUserData,createActivity} from '../server';
import FriendItem from './friendItem';
import {hashHistory} from 'react-router';
import AvatarCropper from "react-avatar-cropper";
import {hideElement} from '../util';
// var debug = require('react-debug');
import {socket,getToken} from '../credentials';

export default class PostActivity extends React.Component {

  constructor(props){
    super(props);
    this.state = {
      userData: {},
      type:"",
      title: "",
      img:null,
      cropperOpen:false,
      startTime: '',
      endTime: '',
      description: "",
      location: "",
      detail:"",
      alert:false,
      sizealert:false
    }
  }

  handleFile(e){
    e.preventDefault();
    // Read the first file that the user selected (if the user selected multiple
    // files, we ignore the others).
    var reader = new FileReader();
    var file = e.target.files[0];
    if(file.size<150000 && file.type.match('image.*')){
      // Called once the browser finishes loading the image.
      reader.onload = (upload) => {
        this.setState({
          img: upload.target.result,
          cropperOpen:true
        });
      };
      reader.readAsDataURL(file);
    }
    else{
      this.setState({sizealert:true});
    }
  }

  handleFileClick(e){
    e.target.value = null;
  }

  handleRequestHide(e){
    e.preventDefault();
    this.setState({
      cropperOpen: false
    })
  }

  handleCrop(dataURI) {
    this.setState({
      cropperOpen: false,
      img: dataURI
    });
  }

  getData(){
    getUserData(this.props.user,(userData)=>{
      this.setState({
        userData: userData
      })
    });
  }

  handleSubmit(e){
    e.preventDefault();
    if(this.state.type!=="------Select a Activity Type-----"&&
        this.state.title.trim()!=="" &&
        this.state.startTime.trim()!=="" &&
        this.state.endTime.trim()!==""&&
        this.state.description.trim()!==""&&
        this.state.location.trim()!==""&&
        this.state.detail.trim()!==""
    ){
      createActivity(this.state,()=>{
        socket.emit('newActivity',{authorization:getToken(),user:this.props.user});
        hashHistory.push('/activity');
      });
    }
    else{
      this.setState({alert:true})
    }
  }

  componentDidMount(){
    this.getData();
  }

  handleTitle(e){
    e.preventDefault();
    this.setState({
      title: e.target.value
    })
  }

  handleStartTime(e){
    e.preventDefault();
    this.setState({
      startTime: e.target.value
    })
  }

  handleEndTime(e){
    e.preventDefault();
    this.setState({
      endTime: e.target.value
    })
  }

  handleLocation(e){
    e.preventDefault();
    this.setState({
      location: e.target.value
    })
  }
  handleDetail(e){
    e.preventDefault();
    this.setState({
      detail: e.target.value
    })
  }

  handleEvent(e){
    e.preventDefault();
    this.setState({
      type: e.target.value
    })
  }
  handleDescription(e){
    e.preventDefault();
    this.setState({
      description: e.target.value
    })
  }

  render() {
    return (

        {this.state.cropperOpen &&
          <AvatarCropper
            onRequestHide={(e)=>this.handleRequestHide(e)}
            cropperOpen={this.state.cropperOpen}
            onCrop={(e)=>this.handleCrop(e)}
            image={this.state.img}
            width={1800}
            height={500}
            />
        }
        <Navbar user={this.state.userData}/>

Create Activity

Activity Info

                                        Please fill in blanks

                                        File is too large

                            <input type="text" id="" className="form-control"
                              value={this.state.title}
                              onChange={(e)=>this.handleTitle(e)}/>
                            Title

Start Time

                            <input type="datetime-local" id="" className="form-control"
                              value={this.state.startTime}
                              onChange={(e)=>this.handleStartTime(e)}
                              placeholder="this"/>

End Time

                            <input type="datetime-local" id="" className="form-control"
                              value={this.state.endTime}
                              onChange={(e)=>this.handleEndTime(e)}/>

                            <input type="text" id="" className="form-control"
                              value={this.state.location}
                              onChange={(e)=>this.handleLocation(e)}/>
                            Location

                            <select className="form-control select" value={this.state.type} onChange={(e)=>this.handleEvent(e)}>
                              <option>------Select a Activity Type-----</option>
                              <option>Event</option>
                              <option>Entertainment</option>
                              <option>Study</option>
                            </select>

                            <textarea type="text" id="" className="md-textarea"
                              value={this.state.description}
                              onChange={(e)=>this.handleDescription(e)}></textarea>
                            Description

                            <textarea type="text" id="" className="md-textarea"
                              value={this.state.detail}
                              onChange={(e)=>this.handleDetail(e)}></textarea>
                            Details

                        <button type="button" className="btn btn-blue-grey pull-Left nomargin" name="button" data-toggle="modal" data-target="#invitemodal">Invite Friend</button>

                                <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

Invite friends

                                  {this.state.userData.friends === undefined ? null : this.state.userData.friends.map((friend,i)=>{
                                    return <FriendItem data={friend} key={i}/>
                                  })}

                            <button type="button" className="btn btn-default btn-blue-grey" data-dismiss="modal">Close</button>
                            <button type="button" className="btn btn-primary btn-blue-grey">Confirm</button>

                      Upload activity header <input type="file" style={{"display":"none"}}
                      onClick={(e)=>this.handleFileClick(e)} onChange={(e)=>this.handleFile(e)}/>

                    <button type="button" className="btn btn-blue-grey pull-right nomargin" onClick={(e)=>this.handleSubmit(e)}>Submit</button>

)
}
}
<commit_msg>update create activity<commit_after>import React from 'react';
import Navbar from '../component/navbar';
import {getUserData,createActivity} from '../server';
import FriendItem from './friendItem';
import {hashHistory} from 'react-router';
import AvatarCropper from "react-avatar-cropper";
import {hideElement} from '../util';
// var debug = require('react-debug');
import {socket,getToken} from '../credentials';

export default class PostActivity extends React.Component {

  constructor(props){
    super(props);
    this.state = {
      userData: {},
      type:"",
      title: "",
      img:null,
      cropperOpen:false,
      startTime: '',
      endTime: '',
      description: "",
      location: "",
      detail:"",
      alert:false,
      sizealert:false,
      fileWrongType:false
    }
  }

  handleFile(e){
    e.preventDefault();
    // Read the first file that the user selected (if the user selected multiple
    // files, we ignore the others).
    var reader = new FileReader();
    var file = e.target.files[0];
    if(!file.type.match('image.*')){
      this.setState({fileWrongType:true});
    }
    else if(file.size<150000){
      // Called once the browser finishes loading the image.
      reader.onload = (upload) => {
        this.setState({
          img: upload.target.result,
          cropperOpen:true
        });
      };
      reader.readAsDataURL(file);
      this.setState({sizealert:false});
        this.setState({fileWrongType:false});
    }
    else{
      this.setState({sizealert:true});
    }
  }

  handleFileClick(e){
    e.target.value = null;
  }

  handleRequestHide(e){
    e.preventDefault();
    this.setState({
      cropperOpen: false
    })
  }

  handleCrop(dataURI) {
    this.setState({
      cropperOpen: false,
      img: dataURI
    });
  }

  getData(){
    getUserData(this.props.user,(userData)=>{
      this.setState({
        userData: userData
      })
    });
  }

  handleSubmit(e){
    e.preventDefault();
    if(this.state.type!=="------Select a Activity Type-----"&&
        this.state.title.trim()!=="" &&
        this.state.startTime.trim()!=="" &&
        this.state.endTime.trim()!==""&&
        this.state.description.trim()!==""&&
        this.state.location.trim()!==""&&
        this.state.detail.trim()!==""
    ){
      createActivity(this.state,()=>{
        socket.emit('newActivity',{authorization:getToken(),user:this.props.user});
        hashHistory.push('/activity');
      });
    }
    else{
      this.setState({alert:true})
    }
  }

  componentDidMount(){
    this.getData();
  }

  handleTitle(e){
    e.preventDefault();
    this.setState({
      title: e.target.value
    })
  }

  handleStartTime(e){
    e.preventDefault();
    this.setState({
      startTime: e.target.value
    })
  }

  handleEndTime(e){
    e.preventDefault();
    this.setState({
      endTime: e.target.value
    })
  }

  handleLocation(e){
    e.preventDefault();
    this.setState({
      location: e.target.value
    })
  }
  handleDetail(e){
    e.preventDefault();
    this.setState({
      detail: e.target.value
    })
  }

  handleEvent(e){
    e.preventDefault();
    this.setState({
      type: e.target.value
    })
  }
  handleDescription(e){
    e.preventDefault();
    this.setState({
      description: e.target.value
    })
  }

  render() {
    return (

        {this.state.cropperOpen &&
          <AvatarCropper
            onRequestHide={(e)=>this.handleRequestHide(e)}
            cropperOpen={this.state.cropperOpen}
            onCrop={(e)=>this.handleCrop(e)}
            image={this.state.img}
            width={1800}
            height={500}
            />
        }
        <Navbar user={this.state.userData}/>

Create Activity

Activity Info

                                        Please fill in blanks

                                        File is too large

                          File is not a image file

                            <input type="text" id="" className="form-control"
                              value={this.state.title}
                              onChange={(e)=>this.handleTitle(e)}/>
                            Title

Start Time

                            <input type="datetime-local" id="" className="form-control"
                              value={this.state.startTime}
                              onChange={(e)=>this.handleStartTime(e)}
                              placeholder="this"/>

End Time

                            <input type="datetime-local" id="" className="form-control"
                              value={this.state.endTime}
                              onChange={(e)=>this.handleEndTime(e)}/>

                            <input type="text" id="" className="form-control"
                              value={this.state.location}
                              onChange={(e)=>this.handleLocation(e)}/>
                            Location

                            <select className="form-control select" value={this.state.type} onChange={(e)=>this.handleEvent(e)}>
                              <option>------Select a Activity Type-----</option>
                              <option>Event</option>
                              <option>Entertainment</option>
                              <option>Study</option>
                            </select>

                            <textarea type="text" id="" className="md-textarea"
                              value={this.state.description}
                              onChange={(e)=>this.handleDescription(e)}></textarea>
                            Description

                            <textarea type="text" id="" className="md-textarea"
                              value={this.state.detail}
                              onChange={(e)=>this.handleDetail(e)}></textarea>
                            Details

                        <button type="button" className="btn btn-blue-grey pull-Left nomargin" name="button" data-toggle="modal" data-target="#invitemodal">Invite Friend</button>

                                <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

Invite friends

                                  {this.state.userData.friends === undefined ? null : this.state.userData.friends.map((friend,i)=>{
                                    return <FriendItem data={friend} key={i}/>
                                  })}

                            <button type="button" className="btn btn-default btn-blue-grey" data-dismiss="modal">Close</button>
                            <button type="button" className="btn btn-primary btn-blue-grey">Confirm</button>

                      Upload activity header <input type="file" style={{"display":"none"}}
                      onClick={(e)=>this.handleFileClick(e)} onChange={(e)=>this.handleFile(e)}/>

                    <button type="button" className="btn btn-blue-grey pull-right nomargin" onClick={(e)=>this.handleSubmit(e)}>Submit</button>

)
}
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Test TripCtrl
 */
describe('Controller: TripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var TripCtrl, $scope, $httpBackend, $location;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, _$location_) {

    $httpBackend = _$httpBackend_;
    $location = _$location_;
    $scope = $rootScope.$new();
    TripCtrl = $controller('TripCtrl', {
      $scope: $scope
    });
  }));

	it('call TripCtrl', inject(function() {
    $httpBackend.expectGET('/api/trips').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.flush();

    expect($scope.trips).toEqual([{stays: 'test'}]);
	}));

  it('test addTrip function', inject(function() {
    $scope.addTrip();
    expect($location.path()).toBe('/trip/new');
  }));
});

/**
 * Test CreateTripCtrl
 */
 describe('Controller: CreateTripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var CreateTripCtrl, $scope, $httpBackend, $location, stayWithOneElements, stayWithTwoElements;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, _$location_) {

    $httpBackend = _$httpBackend_;
    $scope = $rootScope.$new();
    $location = _$location_;

    CreateTripCtrl = $controller('CreateTripCtrl', {
      $scope: $scope
    });

    stayWithOneElements = [{
      destination: '',
      client: '',
      destinationTime: new Date()
    }];

    stayWithTwoElements = [{
      destination: '',
      client: '',
      destinationTime: new Date()
    }, {
      destination: '',
      client: '',
      destinationTime: new Date()
    }];

  }));

  it('call CreateTripCtrl', inject(function() {

    $httpBackend.expectGET('/api/accounts').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.expectGET('/api/cars').respond([{stays: [{destination: 'test'}]}]);
    // !!!
    $httpBackend.expectGET('/api/cars/undefined').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.flush();

    // @todo
    // console.log($scope.accounts);
    // console.log($scope.cars);
    // console.log($scope.trip.car);
    // console.log($scope.trip.kilometerStart);
  }));

  it('test sync function', inject(function() {
    $scope.trip.car = {mileage: 42};
    $scope.sync();

    expect($scope.trip.kilometerStart).toBe(42);
  }));

  it('test addStay function', inject(function() {
    $scope.stays = stayWithOneElements;
    $scope.addStay();

    // expect($scope.stays).toEqual(stayWithTwoElements);
    expect(angular.equals($scope.stays, stayWithTwoElements)).toBe(true);
  }));

  it('test removeStay function', inject(function() {
    $scope.stays = stayWithTwoElements;
    $scope.removeStay($scope.stays[0]);

    // expect($scope.stays).toEqual(stayWithOneElements);
    expect(angular.equals($scope.stays, stayWithOneElements)).toBe(true);
  }));

  // @todo
  // it('test addTrip function', inject(function() {

  //   $httpBackend.expectGET('/api/accounts').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.expectGET('/api/cars').respond([{stays: [{destination: "test"}]}]);
  //   // !!!
  //   $httpBackend.expectGET('/api/cars/undefined').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.flush();

  //   $scope.stays = stayWithTwoElements;
  //   $httpBackend.expectPOST('/api/stays').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.expectPOST('/api/stays').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.flush();
  // }));
});

/**
 * Test DetailTripCtrl
 */
describe('Controller: DetailTripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var DetailTripCtrl, $scope, $httpBackend, $location, testTrip;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, $routeParams, _$location_) {

    $httpBackend = _$httpBackend_;
    $scope = $rootScope.$new();
    $location = _$location_;

    DetailTripCtrl = $controller('DetailTripCtrl', {
      $scope: $scope,
      $routeParams: {id: 1}
    });
    testTrip = {_id: 1, stays: [{destination: 'test'}]};
  }));

  it('should get trip with destinations', inject(function() {

    $httpBackend.expectGET('/api/trips/' + testTrip._id).respond(testTrip);
    $httpBackend.flush();

    expect($scope.trip).toEqual({_id: 1, stays: [{destination: 'test'}]});
    expect($scope.destinations).toEqual('test');
  }));

  it('test returnToOverview function', inject(function() {
    $scope.returnToOverview();
    expect($location.path()).toBe('/trip');
  }));
});

<commit_msg>testing jenkins without tests 2<commit_after>'use strict';

/**
 * Test TripCtrl
 */
describe('Controller: TripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var TripCtrl, $scope, $httpBackend, $location;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, _$location_) {

    $httpBackend = _$httpBackend_;
    $location = _$location_;
    $scope = $rootScope.$new();
    TripCtrl = $controller('TripCtrl', {
      $scope: $scope
    });
  }));

	it('call TripCtrl', inject(function() {
    $httpBackend.expectGET('/api/trips').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.flush();

    expect($scope.trips).toEqual([{stays: 'test'}]);
	}));

  it('test addTrip function', inject(function() {
    $scope.addTrip();
    expect($location.path()).toBe('/trip/new');
  }));
});

/**
 * Test CreateTripCtrl
 */
 describe('Controller: CreateTripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var CreateTripCtrl, $scope, $httpBackend, $location, stayWithOneElements, stayWithTwoElements;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, _$location_) {

    $httpBackend = _$httpBackend_;
    $scope = $rootScope.$new();
    $location = _$location_;

    CreateTripCtrl = $controller('CreateTripCtrl', {
      $scope: $scope
    });

    stayWithOneElements = [{
      destination: '',
      client: '',
      destinationTime: new Date()
    }];

    stayWithTwoElements = [{
      destination: '',
      client: '',
      destinationTime: new Date()
    }, {
      destination: '',
      client: '',
      destinationTime: new Date()
    }];

  }));

  it('call CreateTripCtrl', inject(function() {

    $httpBackend.expectGET('/api/accounts').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.expectGET('/api/cars').respond([{stays: [{destination: 'test'}]}]);
    // @todo
    $httpBackend.expectGET('/api/cars/undefined').respond([{stays: [{destination: 'test'}]}]);
    $httpBackend.flush();

    // @todo
    // console.log($scope.accounts);
    // console.log($scope.cars);
    // console.log($scope.trip.car);
    // console.log($scope.trip.kilometerStart);
  }));

  it('test sync function', inject(function() {
    $scope.trip.car = {mileage: 42};
    $scope.sync();

    expect($scope.trip.kilometerStart).toBe(42);
  }));

  it('test addStay function', inject(function() {
    $scope.stays = stayWithOneElements;
    $scope.addStay();

    // expect($scope.stays).toEqual(stayWithTwoElements);
    expect(angular.equals($scope.stays, stayWithTwoElements)).toBe(true);
  }));

  it('test removeStay function', inject(function() {
    $scope.stays = stayWithTwoElements;
    $scope.removeStay($scope.stays[0]);

    // expect($scope.stays).toEqual(stayWithOneElements);
    expect(angular.equals($scope.stays, stayWithOneElements)).toBe(true);
  }));

  // @todo
  // it('test addTrip function', inject(function() {

  //   $httpBackend.expectGET('/api/accounts').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.expectGET('/api/cars').respond([{stays: [{destination: "test"}]}]);
  //   // !!!
  //   $httpBackend.expectGET('/api/cars/undefined').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.flush();

  //   $scope.stays = stayWithTwoElements;
  //   $httpBackend.expectPOST('/api/stays').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.expectPOST('/api/stays').respond([{stays: [{destination: "test"}]}]);
  //   $httpBackend.flush();
  // }));
});

/**
 * Test DetailTripCtrl
 */
describe('Controller: DetailTripCtrl', function () {

  beforeEach(module('fahrtenbuchApp'));
  var DetailTripCtrl, $scope, $httpBackend, $location, testTrip;

  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, $routeParams, _$location_) {

    $httpBackend = _$httpBackend_;
    $scope = $rootScope.$new();
    $location = _$location_;

    DetailTripCtrl = $controller('DetailTripCtrl', {
      $scope: $scope,
      $routeParams: {id: 1}
    });
    testTrip = {_id: 1, stays: [{destination: 'test'}]};
  }));

  it('should get trip with destinations', inject(function() {

    $httpBackend.expectGET('/api/trips/' + testTrip._id).respond(testTrip);
    $httpBackend.flush();

    expect($scope.trip).toEqual({_id: 1, stays: [{destination: 'test'}]});
    expect($scope.destinations).toEqual('test');
  }));

  it('test returnToOverview function', inject(function() {
    $scope.returnToOverview();
    expect($location.path()).toBe('/trip');
  }));
});

<|endoftext|>


<commit_before>goog.provide('goats.runtime.filters');

goog.require('goog.object');
goog.require('goog.math');
goog.require('goog.string');
goog.require('goog.string.format');

/**
 * Outputs debug information to console.
 *
 * @param {*} input The input.
 */
goats.runtime.filters.debug = function(input) {
	window.console.log(input);
};

/**
 * Returns the length of the input.
 *
 * @param {Array|Object} input The input array or object.
 * @returns {number} the length of the input.
 */
goats.runtime.filters.length = function(input) {
	if (input) {
		if (input.length) {
			return input.length;
		} else {
			return goog.object.getCount(input);
		}
	}
	return 0;
};

/**
 * An alias of length().
 */
goats.runtime.filters.len = goats.runtime.filters.length;

/**
 * Converts the input to a title (capitalize the frist letter of each word).
 *
 * @param {string} input The input.
 * @returns {string} The title.
 */
goats.runtime.filters.title = function(input) {
	return goog.string.toTitleCase(input);
};

/**
 * Centers the input string with the given width.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.center = function(input, width) {
	if (input.length >= width) {
		return input;
	}
	var extra = input.length;
	var left = extra / 2;
	var right = extra - left;

	return goats.runtime.filters.fillSpaces(left) + input +
		goats.runtime.filters.fillSpaces(right);
};

/**
 * Justifies the string to the left.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.ljust = function(input, width) {
	return goats.runtime.filters.just(input, width, true /* left */);
};

/**
 * Justifies the string to the right.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.rjust = function(input, width) {
	return goats.runtime.filters.just(input, width, false /* left */);
};

/**
 * Cuts a substring from the input string.
 *
 * @param {string} input The input.
 * @param {string} removed The string to remove.
 * @returns {string} The result.
 */
goats.runtime.filters.cut = function(input, removed) {
	return goog.string.remove(input, removed);
};

/**
 * Joins an array of strings.
 *
 * @param {Array.<string>} input The array of strings.
 * @param {string} separator The separator.
 * @returns {string} The result.
 */
goats.runtime.filters.join = function(input, separator) {
	return input.join(separator);
};

/**
 * Formats the float number to string.
 *
 * @param {number} input The float number.
 * @param {number} precision The precision.
 * @returns {string} The result.
 */
goats.runtime.filters.floatformat = function(input, precision) {
	if (precision < 0) {
		precision = -precision;
	}
	return input.toFixed(precision);
};

/**
 * Quotes the input string.
 *
 * @param {string} input The input.
 * @returns {string} The result.
 */
goats.runtime.filters.quote = function(input) {
	return goog.string.quote(input);
};

/**
 * Formats the input.
 *
 * @param {string} f The format string.
 * @param {...string|number} var_args Values formatString is to be filled with.
 * @returns {string} The result.
 */
goats.runtime.filters.format = function(f, var_args) {
	var args = Array.prototype.slice.call(arguments);
	return goog.string.format.apply(null, args);
};

/**
 * Parse integer from string or integer.
 *
 * @param {string} val the string or integer.
 * @returns {string} The result.
 */
goats.runtime.filters.parseInt = function(val) {
	if (goog.math.isInt(val)) {
		return val;
	}
        return goog.string.parseInt(val);
};

// ================ utility functions ================

/**
 * @const
 * @private
 */
var SPACES = '                                                                                ';

/**
 * Generate space string with the given width.
 *
 * @param {number} width The width.
 * @returns {number|string} The result.
 */
goats.runtime.filters.fillSpaces = function(width) {
	if (width == SPACES.length) {
		return SPACES;
	} else if (width < SPACES.length) {
		return SPACES.slice(0, width);
	}

	var n = width / SPACES.length;
	var r = width % SPACES.length;
	var s = "";
	for (var i = 0; i < n; i++) {
		// It's not optimal in complexity, but in practice it's very rare
		// to have to create such a long empty string.
		s += SPACES;
	}
	if (r > 0) {
		s =+ goats.runtime.filters.fillSpaces(r);
	}
	return s;
};

/**
 * Justifies the string to left or right.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @param {boolean} left Whether justify to left.
 * @returns {string} The result.
 */
goats.runtime.filters.just = function(input, width, left) {
	if (input.length < width) {
		var extra = width - input.length;
		if (left) {
			input += goats.runtime.filters.fillSpaces(extra);
		} else {
			input = goats.runtime.filters.fillSpaces(extra) + input;
		}
	}
	return input;
};
<commit_msg>Do format<commit_after>goog.provide('goats.runtime.filters');

goog.require('goog.object');
goog.require('goog.math');
goog.require('goog.string');
goog.require('goog.string.format');

/**
 * Outputs debug information to console.
 *
 * @param {*} input The input.
 */
goats.runtime.filters.debug = function(input) {
	window.console.log(input);
};

/**
 * Returns the length of the input.
 *
 * @param {Array|Object} input The input array or object.
 * @returns {number} the length of the input.
 */
goats.runtime.filters.length = function(input) {
	if (input) {
		if (input.length) {
			return input.length;
		} else {
			return goog.object.getCount(input);
		}
	}
	return 0;
};

/**
 * An alias of length().
 */
goats.runtime.filters.len = goats.runtime.filters.length;

/**
 * Converts the input to a title (capitalize the frist letter of each word).
 *
 * @param {string} input The input.
 * @returns {string} The title.
 */
goats.runtime.filters.title = function(input) {
	return goog.string.toTitleCase(input);
};

/**
 * Centers the input string with the given width.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.center = function(input, width) {
	if (input.length >= width) {
		return input;
	}
	var extra = input.length;
	var left = extra / 2;
	var right = extra - left;

	return goats.runtime.filters.fillSpaces(left) + input +
		goats.runtime.filters.fillSpaces(right);
};

/**
 * Justifies the string to the left.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.ljust = function(input, width) {
	return goats.runtime.filters.just(input, width, true /* left */);
};

/**
 * Justifies the string to the right.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @returns {string} The result.
 */
goats.runtime.filters.rjust = function(input, width) {
	return goats.runtime.filters.just(input, width, false /* left */);
};

/**
 * Cuts a substring from the input string.
 *
 * @param {string} input The input.
 * @param {string} removed The string to remove.
 * @returns {string} The result.
 */
goats.runtime.filters.cut = function(input, removed) {
	return goog.string.remove(input, removed);
};

/**
 * Joins an array of strings.
 *
 * @param {Array.<string>} input The array of strings.
 * @param {string} separator The separator.
 * @returns {string} The result.
 */
goats.runtime.filters.join = function(input, separator) {
	return input.join(separator);
};

/**
 * Formats the float number to string.
 *
 * @param {number} input The float number.
 * @param {number} precision The precision.
 * @returns {string} The result.
 */
goats.runtime.filters.floatformat = function(input, precision) {
	if (precision < 0) {
		precision = -precision;
	}
	return input.toFixed(precision);
};

/**
 * Quotes the input string.
 *
 * @param {string} input The input.
 * @returns {string} The result.
 */
goats.runtime.filters.quote = function(input) {
	return goog.string.quote(input);
};

/**
 * Formats the input.
 *
 * @param {string} f The format string.
 * @param {...string|number} var_args Values formatString is to be filled with.
 * @returns {string} The result.
 */
goats.runtime.filters.format = function(f, var_args) {
	var args = Array.prototype.slice.call(arguments);
	return goog.string.format.apply(null, args);
};

/**
 * Parse integer from string or integer.
 *
 * @param {string} val the string or integer.
 * @returns {string} The result.
 */
goats.runtime.filters.parseInt = function(val) {
	if (goog.math.isInt(val)) {
		return val;
	}
	return goog.string.parseInt(val);
};

// ================ utility functions ================

/**
 * @const
 * @private
 */
var SPACES = '                                                                                ';

/**
 * Generate space string with the given width.
 *
 * @param {number} width The width.
 * @returns {number|string} The result.
 */
goats.runtime.filters.fillSpaces = function(width) {
	if (width == SPACES.length) {
		return SPACES;
	} else if (width < SPACES.length) {
		return SPACES.slice(0, width);
	}

	var n = width / SPACES.length;
	var r = width % SPACES.length;
	var s = "";
	for (var i = 0; i < n; i++) {
		// It's not optimal in complexity, but in practice it's very rare
		// to have to create such a long empty string.
		s += SPACES;
	}
	if (r > 0) {
		s =+ goats.runtime.filters.fillSpaces(r);
	}
	return s;
};

/**
 * Justifies the string to left or right.
 *
 * @param {string} input The input.
 * @param {number} width The width.
 * @param {boolean} left Whether justify to left.
 * @returns {string} The result.
 */
goats.runtime.filters.just = function(input, width, left) {
	if (input.length < width) {
		var extra = width - input.length;
		if (left) {
			input += goats.runtime.filters.fillSpaces(extra);
		} else {
			input = goats.runtime.filters.fillSpaces(extra) + input;
		}
	}
	return input;
};
<|endoftext|>


<commit_before>"use strict";

const Package = require("@lerna/package");

// file under test
const PackageGraph = require("..");

describe("PackageGraph", () => {
  describe("constructor", () => {
    it("throws an error when duplicate package names are present", () => {
      const pkgs = [
        new Package({ name: "pkg-1", version: "1.0.0" }, "/test/pkg-1", "/test"),
        new Package({ name: "pkg-2", version: "2.0.0" }, "/test/pkg-2", "/test"),
        new Package({ name: "pkg-2", version: "3.0.0" }, "/test/pkg-3", "/test"),
      ];

      try {
        // eslint-disable-next-line no-unused-vars
        const graph = new PackageGraph(pkgs);
      } catch (err) {
        expect(err.prefix).toBe("ENAME");
        expect(err.message).toMatchInlineSnapshot(`
"Package name \\"pkg-2\\" used in multiple packages:
	/test/pkg-2
	/test/pkg-3"
`);
      }

      expect.assertions(2);
    });
  });

  describe("Node", () => {
    it("proxies Package properties", () => {
      const pkg = new Package({ name: "my-pkg", version: "1.2.3" }, "/path/to/my-pkg");
      const graph = new PackageGraph([pkg]);
      const node = graph.get("my-pkg");

      // most of these properties are non-enumerable, so a snapshot doesn't work
      expect(node.name).toBe("my-pkg");
      expect(node.location).toBe("/path/to/my-pkg");
      expect(node.prereleaseId).toBeUndefined();
      expect(node.version).toBe("1.2.3");
      expect(node.pkg).toBe(pkg);
    });

    it("exposes graph-specific Map properties", () => {
      const node = new PackageGraph([
        new Package({ name: "my-pkg", version: "4.5.6" }, "/path/to/my-pkg"),
      ]).get("my-pkg");

      expect(node).toHaveProperty("externalDependencies", expect.any(Map));
      expect(node).toHaveProperty("localDependencies", expect.any(Map));
      expect(node).toHaveProperty("localDependents", expect.any(Map));
    });

    it("computes prereleaseId from prerelease version", () => {
      const node = new PackageGraph([
        new Package({ name: "my-pkg", version: "1.2.3-rc.4" }, "/path/to/my-pkg"),
      ]).get("my-pkg");

      expect(node.prereleaseId).toBe("rc");
    });
  });

  describe(".get()", () => {
    it("should return a node with localDependencies", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            dependencies: {
              "external-thing": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            devDependencies: {
              "my-package-1": "^1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages, "allDependencies");

      expect(graph.get("my-package-1").localDependencies.size).toBe(0);
      expect(graph.get("my-package-2").localDependencies.has("my-package-1")).toBe(true);
    });

    it("should skip gitCommittish of packages that are not in localDependencies", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            devDependencies: {
              "my-package-2": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            dependencies: {
              "external-thing": "github:user-foo/project-foo#v1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages, "dependencies");

      expect(graph.get("my-package-1").localDependencies.size).toBe(0);
      expect(graph.get("my-package-2").localDependencies.size).toBe(0);
    });

    it("should return the localDependencies for matched gitCommittish", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            dependencies: {
              "external-thing": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            devDependencies: {
              "my-package-1": "github:user-foo/project-foo#v1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages);

      expect(graph.get("my-package-2").localDependencies.has("my-package-1")).toBe(true);
    });
  });

  describe(".partitionCycles()", () => {
    it("does not mutate a graph with no cycles", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            dependencies: {
              "pkg-1": "^1.0.0",
            },
          },
          "/test/pkg-2"
        ),
      ];
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();

      expect(graph.size).toBe(2);
      expect(paths.size).toBe(0);
      expect(nodes.size).toBe(0);
    });

    it("prunes direct cycles from the graph", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
            dependencies: {
              "pkg-2": "^2.0.0",
            },
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            dependencies: {
              "pkg-1": "^1.0.0",
            },
          },
          "/test/pkg-2"
        ),
      ];
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();
      // deepInspect(nodes);

      expect(graph.size).toBe(0);
      expect(nodes.size).toBe(2);
      expect(paths).toMatchInlineSnapshot(`
Set {
  Array [
    "pkg-1",
    "pkg-2",
    "pkg-1",
  ],
  Array [
    "pkg-2",
    "pkg-1",
    "pkg-2",
  ],
}
`);
    });

    it("prunes all cycles from the graph, retaining non-cycles", () => {
      const pkgs = topoPackages();
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();
      // deepInspect(graph);
      // deepInspect(nodes);

      expect(Array.from(graph.keys())).toMatchInlineSnapshot(`
Array [
  "dag-1",
  "dag-2a",
  "dag-2b",
  "dag-3",
  "standalone",
]
`);
      expect(Array.from(nodes.keys()).map(node => node.name)).toMatchInlineSnapshot(`
Array [
  "cycle-1",
  "cycle-2",
  "cycle-tiebreaker",
]
`);
      expect(paths).toMatchInlineSnapshot(`
Set {
  Array [
    "cycle-1",
    "cycle-2",
    "cycle-1",
  ],
  Array [
    "cycle-2",
    "cycle-1",
    "cycle-2",
  ],
  Array [
    "cycle-tiebreaker",
    "cycle-1",
    "cycle-2",
    "cycle-1",
  ],
}
`);
    });
  });
});

// eslint-disable-next-line no-unused-vars
function deepInspect(obj) {
  // jest console mutilates console.dir() options argument,
  // so sidestep it by requiring the non-shimmed method directly.
  // eslint-disable-next-line global-require
  require("console").dir(obj, { depth: 10, compact: false });
}

function topoPackages() {
  return [
    {
      name: "cycle-1",
      version: "1.0.0",
      dependencies: {
        "cycle-2": "1.0.0",
      },
    },
    {
      name: "cycle-2",
      version: "1.0.0",
      dependencies: {
        "cycle-1": "1.0.0",
      },
    },
    {
      name: "cycle-tiebreaker",
      version: "1.0.0",
      description: "Breaks ties between cycle-{1,2} when batching.",
      dependencies: {
        "cycle-1": "1.0.0",
      },
    },
    {
      name: "dag-1",
      version: "1.0.0",
    },
    {
      name: "dag-2a",
      version: "1.0.0",
      dependencies: {
        "dag-1": "1.0.0",
      },
    },
    {
      name: "dag-2b",
      version: "1.0.0",
      dependencies: {
        "dag-1": "1.0.0",
      },
    },
    {
      name: "dag-3",
      version: "1.0.0",
      dependencies: {
        "dag-2a": "1.0.0",
        "dag-1": "1.0.0",
      },
    },
    {
      name: "standalone",
      version: "1.0.0",
    },
  ].map(json => new Package(json, `/test/${json.name}`, "/test"));
}
<commit_msg>test(package-graph): Full unit coverage<commit_after>"use strict";

const Package = require("@lerna/package");

// file under test
const PackageGraph = require("..");

describe("PackageGraph", () => {
  describe("constructor", () => {
    it("throws an error when duplicate package names are present", () => {
      const pkgs = [
        new Package({ name: "pkg-1", version: "1.0.0" }, "/test/pkg-1", "/test"),
        new Package({ name: "pkg-2", version: "2.0.0" }, "/test/pkg-2", "/test"),
        new Package({ name: "pkg-2", version: "3.0.0" }, "/test/pkg-3", "/test"),
      ];

      try {
        // eslint-disable-next-line no-unused-vars
        const graph = new PackageGraph(pkgs);
      } catch (err) {
        expect(err.prefix).toBe("ENAME");
        expect(err.message).toMatchInlineSnapshot(`
"Package name \\"pkg-2\\" used in multiple packages:
	/test/pkg-2
	/test/pkg-3"
`);
      }

      expect.assertions(2);
    });

    it("externalizes non-satisfied semver of local sibling", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
            optionalDependencies: {
              // non-circular external
              "pkg-2": "^1.0.0",
            },
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            devDependencies: {
              "pkg-1": "^1.0.0",
            },
          },
          "/test/pkg-2"
        ),
        new Package(
          {
            name: "pkg-3",
            version: "3.0.0",
            dependencies: {
              "pkg-2": "^2.0.0",
            },
          },
          "/test/pkg-3"
        ),
      ];
      const graph = new PackageGraph(pkgs);

      // deepInspect(graph);
      expect(graph.get("pkg-1").externalDependencies.has("pkg-2")).toBe(true);
      expect(graph.get("pkg-2").localDependents.has("pkg-1")).toBe(false);
      expect(graph.get("pkg-2").localDependencies.has("pkg-1")).toBe(true);
      expect(graph.get("pkg-3").localDependencies.has("pkg-2")).toBe(true);
    });

    it("localizes all non-satisfied siblings when forced", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            dependencies: {
              // non-circular external
              "pkg-1": "^2.0.0",
            },
          },
          "/test/pkg-2"
        ),
      ];
      const graph = new PackageGraph(pkgs, "allDependencies", true);
      // deepInspect(graph);
      const [pkg1, pkg2] = graph.values();

      expect(pkg1.localDependents.has("pkg-2")).toBe(true);
      expect(pkg2.localDependencies.has("pkg-1")).toBe(true);
    });
  });

  describe("Node", () => {
    it("proxies Package properties", () => {
      const pkg = new Package({ name: "my-pkg", version: "1.2.3" }, "/path/to/my-pkg");
      const graph = new PackageGraph([pkg]);
      const node = graph.get("my-pkg");

      // most of these properties are non-enumerable, so a snapshot doesn't work
      expect(node.name).toBe("my-pkg");
      expect(node.location).toBe("/path/to/my-pkg");
      expect(node.prereleaseId).toBeUndefined();
      expect(node.version).toBe("1.2.3");
      expect(node.pkg).toBe(pkg);
    });

    it("exposes graph-specific Map properties", () => {
      const node = new PackageGraph([
        new Package({ name: "my-pkg", version: "4.5.6" }, "/path/to/my-pkg"),
      ]).get("my-pkg");

      expect(node).toHaveProperty("externalDependencies", expect.any(Map));
      expect(node).toHaveProperty("localDependencies", expect.any(Map));
      expect(node).toHaveProperty("localDependents", expect.any(Map));
    });

    it("computes prereleaseId from prerelease version", () => {
      const node = new PackageGraph([
        new Package({ name: "my-pkg", version: "1.2.3-rc.4" }, "/path/to/my-pkg"),
      ]).get("my-pkg");

      expect(node.prereleaseId).toBe("rc");
    });
  });

  describe(".get()", () => {
    it("should return a node with localDependencies", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            dependencies: {
              "external-thing": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            devDependencies: {
              "my-package-1": "^1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages, "allDependencies");

      expect(graph.get("my-package-1").localDependencies.size).toBe(0);
      expect(graph.get("my-package-2").localDependencies.has("my-package-1")).toBe(true);
    });

    it("should skip gitCommittish of packages that are not in localDependencies", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            devDependencies: {
              "my-package-2": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            dependencies: {
              "external-thing": "github:user-foo/project-foo#v1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages, "dependencies");

      expect(graph.get("my-package-1").localDependencies.size).toBe(0);
      expect(graph.get("my-package-2").localDependencies.size).toBe(0);
    });

    it("should return the localDependencies for matched gitCommittish", () => {
      const packages = [
        new Package(
          {
            name: "my-package-1",
            version: "1.0.0",
            dependencies: {
              "external-thing": "^1.0.0",
            },
          },
          "/path/to/package-1"
        ),
        new Package(
          {
            name: "my-package-2",
            version: "1.0.0",
            devDependencies: {
              "my-package-1": "github:user-foo/project-foo#v1.0.0",
            },
          },
          "/path/to/package-2"
        ),
      ];
      const graph = new PackageGraph(packages);

      expect(graph.get("my-package-2").localDependencies.has("my-package-1")).toBe(true);
    });
  });

  describe(".rawPackageList", () => {
    it("retuns an array of Package instances", () => {
      const pkgs = [
        new Package({ name: "pkg-1", version: "1.0.0" }, "/test/pkg-1", "/test"),
        new Package({ name: "pkg-2", version: "2.0.0" }, "/test/pkg-2", "/test"),
      ];
      const graph = new PackageGraph(pkgs);

      expect(graph.rawPackageList).toEqual(pkgs);
    });
  });

  describe.each`
    method               | filtered     | expected
    ${"addDependencies"} | ${["pkg-a"]} | ${["pkg-a", "pkg-b"]}
    ${"addDependents"}   | ${["pkg-d"]} | ${["pkg-d", "pkg-c"]}
  `(".$method()", ({ method, filtered, expected }) => {
    it(`extends ${filtered} to ${expected}`, () => {
      const pkgs = [
        { name: "pkg-a", version: "1.0.0", dependencies: { "pkg-b": "1.0.0" } },
        { name: "pkg-b", version: "1.0.0", dependencies: {} },
        { name: "pkg-c", version: "1.0.0", dependencies: { "pkg-d": "1.0.0" } },
        { name: "pkg-d", version: "1.0.0", dependencies: { "pkg-c": "1.0.0" } },
        // cycle c <-> d catches nested search.add()
      ].map(json => new Package(json, `/test/${json.name}`, "/test"));
      const graph = new PackageGraph(pkgs);

      const search = filtered.map(name => graph.get(name).pkg);
      const result = graph[method](search);

      expect(result.map(pkg => pkg.name)).toEqual(expected);
    });
  });

  describe(".partitionCycles()", () => {
    it("does not mutate a graph with no cycles", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            dependencies: {
              "pkg-1": "^1.0.0",
            },
          },
          "/test/pkg-2"
        ),
      ];
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();

      expect(graph.size).toBe(2);
      expect(paths.size).toBe(0);
      expect(nodes.size).toBe(0);
    });

    it("prunes direct cycles from the graph", () => {
      const pkgs = [
        new Package(
          {
            name: "pkg-1",
            version: "1.0.0",
            dependencies: {
              "pkg-2": "^2.0.0",
            },
          },
          "/test/pkg-1"
        ),
        new Package(
          {
            name: "pkg-2",
            version: "2.0.0",
            dependencies: {
              "pkg-1": "^1.0.0",
            },
          },
          "/test/pkg-2"
        ),
      ];
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();
      // deepInspect(nodes);

      expect(graph.size).toBe(0);
      expect(nodes.size).toBe(2);
      expect(paths).toMatchInlineSnapshot(`
Set {
  Array [
    "pkg-1",
    "pkg-2",
    "pkg-1",
  ],
  Array [
    "pkg-2",
    "pkg-1",
    "pkg-2",
  ],
}
`);
    });

    it("prunes all cycles from the graph, retaining non-cycles", () => {
      const pkgs = topoPackages();
      const graph = new PackageGraph(pkgs);
      // deepInspect(graph);
      const [paths, nodes] = graph.partitionCycles();
      // deepInspect(graph);
      // deepInspect(nodes);

      expect(Array.from(graph.keys())).toMatchInlineSnapshot(`
Array [
  "dag-1",
  "dag-2a",
  "dag-2b",
  "dag-3",
  "standalone",
]
`);
      expect(Array.from(nodes.keys()).map(node => node.name)).toMatchInlineSnapshot(`
Array [
  "cycle-1",
  "cycle-2",
  "cycle-tiebreaker",
]
`);
      expect(paths).toMatchInlineSnapshot(`
Set {
  Array [
    "cycle-1",
    "cycle-2",
    "cycle-1",
  ],
  Array [
    "cycle-2",
    "cycle-1",
    "cycle-2",
  ],
  Array [
    "cycle-tiebreaker",
    "cycle-1",
    "cycle-2",
    "cycle-1",
  ],
}
`);
    });
  });
});

// eslint-disable-next-line no-unused-vars
function deepInspect(obj) {
  // jest console mutilates console.dir() options argument,
  // so sidestep it by requiring the non-shimmed method directly.
  // eslint-disable-next-line global-require
  require("console").dir(obj, { depth: 10, compact: false });
}

function topoPackages() {
  return [
    {
      name: "cycle-1",
      version: "1.0.0",
      dependencies: {
        "cycle-2": "1.0.0",
      },
    },
    {
      name: "cycle-2",
      version: "1.0.0",
      dependencies: {
        "cycle-1": "1.0.0",
      },
    },
    {
      name: "cycle-tiebreaker",
      version: "1.0.0",
      description: "Breaks ties between cycle-{1,2} when batching.",
      dependencies: {
        "cycle-1": "1.0.0",
      },
    },
    {
      name: "dag-1",
      version: "1.0.0",
    },
    {
      name: "dag-2a",
      version: "1.0.0",
      dependencies: {
        "dag-1": "1.0.0",
      },
    },
    {
      name: "dag-2b",
      version: "1.0.0",
      dependencies: {
        "dag-1": "1.0.0",
      },
    },
    {
      name: "dag-3",
      version: "1.0.0",
      dependencies: {
        "dag-2a": "1.0.0",
        "dag-1": "1.0.0",
      },
    },
    {
      name: "standalone",
      version: "1.0.0",
    },
  ].map(json => new Package(json, `/test/${json.name}`, "/test"));
}
<|endoftext|>


<commit_before>// On the shoulders of giants:
// --------------------------
// MDN - Service Worker API - Using Service Workers
// https://serviceworke.rs
// https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

'use strict';

const CACHE_VERSION = 1;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
  css: `css-cache-v${CACHE_VERSION}`,
  html: `html-cache-v${CACHE_VERSION}`, // is this needed?
  img: `img-cache-v${CACHE_VERSION}`,
  js: `js-cache-v${CACHE_VERSION}`
  // json: `json-cache-v${CACHE_VERSION}` // this should be in indexedDB.
  // currently appcache has everything stored in it, including json
  // will eventually need to take out json and move it to indexedDB.
  // indexedDB will also store the user's progress.
};

const CACHE = {
  font: [
    'public/font/Roboto/Roboto-Regular.ttf',
    'public/font/Roboto/Roboto-Medium.ttf',
    'public/font/Roboto/Roboto-Bold.ttf',
    'public/font/Lato/Lato-Black.ttf',
    'public/font/Kalam/Kalam-Regular.ttf'
  ],

  css: [
    'public/css/style.css',
    'public/css/vendor/bootswatch/bootstrap.min.css',
    'public/css/vendor/codemirror/codemirror.min.css',
    'public/css/vendor/codemirror/monokai.min.css'
  ],

  html: ['/'],

  img: [
    'public/img/FCClogo.svg',
    'public/img/FCCfire.svg'
  ],

  js: [
    'public/js/main.bundle.js',
    'public/js/index.bundle.js',
    'public/js/ww.bundle.js',
    'public/js/sw.bundle.js'
  ]
};

// challenges currently do not need to be imported as it's part of the main.bundle.js

self.addEventListener('install', event => {
  console.log('in the install event');
  event.waitUntil(
    Object.keys(CURRENT_CACHES).forEach(key => {
      caches
        .open(CURRENT_CACHES[key])
        .then(cache => cache.addAll(CACHE[key]))
        .then(() => {
          console.log(`${key} has been cached.`);
          return self.skipWaiting(); // make queued SW active.
        });
    })
  );
});

self.addEventListener('fetch', event => {
  console.log(`Handling fetch event for ${event.request.url}`);

  let requestType;

  switch (event.request.url.split('.')[event.request.url.split('.').length - 1]) {
    case 'http://localhost:3000/':
    case 'http://localhost:8080/':
    case 'com/':
      requestType = 'html';
      break;
    case 'ttf':
      requestType = 'font';
      break;
    case 'css':
      requestType = 'css';
      break;
    case 'svg':
    case 'png':
    case 'jpeg':
    case 'jpg':
      requestType = 'img';
      break;
    case 'js':
      requestType = 'js';
      break;
    default:
      requestType = 'not found';
  }

  if (requestType === 'not found') {
    console.log(`Requested type, ${event.request.url}, is not in the cache`);
    return fetch(event.request);
  }
  event.respondWith(
    caches.open(CURRENT_CACHES[requestType]).then(cache =>
      /*
      if (event.request.url.includes('/sw.js')) {
        console.log('Request to sw.js detected');
        event.request.url = event.request.url.replace('sw.js', 'public/js/sw.bundle.js');
        console.log(`Changed request: ${event.request.url}`);
      }
      */
      cache.match(event.request).then(res => {
        if (res) {
          console.log(`Response found in cache ${res}.`);
          return res;
        }
        return fetch(event.request);
      })
    )
  );
});

self.addEventListener('activate', event => {
  console.log('[activate]: Activating Service Worker.');
  // Delete all caches that aren't named in CURRENT_CACHES
  const expectedCacheNames = Object.keys(CURRENT_CACHES).map(key => CURRENT_CACHES[key]);

  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(cacheNames.map(cacheName => {
        if (expectedCacheNames.indexOf(cacheName) === -1) {
          console.log(`Deleting out of date cache: ${cacheName}`);
          return caches.delete(cacheName);
        }
      }))
    )
    .then(() => self.clients.claim())
  );
});
<commit_msg>test 'com' ending<commit_after>// On the shoulders of giants:
// --------------------------
// MDN - Service Worker API - Using Service Workers
// https://serviceworke.rs
// https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

'use strict';

const CACHE_VERSION = 1;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
  css: `css-cache-v${CACHE_VERSION}`,
  html: `html-cache-v${CACHE_VERSION}`, // is this needed?
  img: `img-cache-v${CACHE_VERSION}`,
  js: `js-cache-v${CACHE_VERSION}`
  // json: `json-cache-v${CACHE_VERSION}` // this should be in indexedDB.
  // currently appcache has everything stored in it, including json
  // will eventually need to take out json and move it to indexedDB.
  // indexedDB will also store the user's progress.
};

const CACHE = {
  font: [
    'public/font/Roboto/Roboto-Regular.ttf',
    'public/font/Roboto/Roboto-Medium.ttf',
    'public/font/Roboto/Roboto-Bold.ttf',
    'public/font/Lato/Lato-Black.ttf',
    'public/font/Kalam/Kalam-Regular.ttf'
  ],

  css: [
    'public/css/style.css',
    'public/css/vendor/bootswatch/bootstrap.min.css',
    'public/css/vendor/codemirror/codemirror.min.css',
    'public/css/vendor/codemirror/monokai.min.css'
  ],

  html: ['/'],

  img: [
    'public/img/FCClogo.svg',
    'public/img/FCCfire.svg'
  ],

  js: [
    'public/js/main.bundle.js',
    'public/js/index.bundle.js',
    'public/js/ww.bundle.js',
    'public/js/sw.bundle.js'
  ]
};

// challenges currently do not need to be imported as it's part of the main.bundle.js

self.addEventListener('install', event => {
  console.log('in the install event');
  event.waitUntil(
    Object.keys(CURRENT_CACHES).forEach(key => {
      caches
        .open(CURRENT_CACHES[key])
        .then(cache => cache.addAll(CACHE[key]))
        .then(() => {
          console.log(`${key} has been cached.`);
          return self.skipWaiting(); // make queued SW active.
        });
    })
  );
});

self.addEventListener('fetch', event => {
  console.log(`Handling fetch event for ${event.request.url}`);

  let requestType;

  switch (event.request.url.split('.')[event.request.url.split('.').length - 1]) {
    case 'http://localhost:3000/':
    case 'http://localhost:8080/':
    case 'com':
      requestType = 'html';
      break;
    case 'ttf':
      requestType = 'font';
      break;
    case 'css':
      requestType = 'css';
      break;
    case 'svg':
    case 'png':
    case 'jpeg':
    case 'jpg':
      requestType = 'img';
      break;
    case 'js':
      requestType = 'js';
      break;
    default:
      requestType = 'not found';
  }

  if (requestType === 'not found') {
    console.log(`Requested type, ${event.request.url}, is not in the cache`);
    console.log(event.request.url.split('.')[event.request.url.split('.').length - 1]);
    return fetch(event.request);
  }
  event.respondWith(
    caches.open(CURRENT_CACHES[requestType]).then(cache =>
      /*
      if (event.request.url.includes('/sw.js')) {
        console.log('Request to sw.js detected');
        event.request.url = event.request.url.replace('sw.js', 'public/js/sw.bundle.js');
        console.log(`Changed request: ${event.request.url}`);
      }
      */
      cache.match(event.request).then(res => {
        if (res) {
          console.log(`Response found in cache ${res}.`);
          return res;
        }
        return fetch(event.request);
      })
    )
  );
});

self.addEventListener('activate', event => {
  console.log('[activate]: Activating Service Worker.');
  // Delete all caches that aren't named in CURRENT_CACHES
  const expectedCacheNames = Object.keys(CURRENT_CACHES).map(key => CURRENT_CACHES[key]);

  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(cacheNames.map(cacheName => {
        if (expectedCacheNames.indexOf(cacheName) === -1) {
          console.log(`Deleting out of date cache: ${cacheName}`);
          return caches.delete(cacheName);
        }
      }))
    )
    .then(() => self.clients.claim())
  );
});
<|endoftext|>