<commit_before>/* global describe, beforeEach, it */
'use strict';
import assert from 'assert';
import {expect} from 'chai';
import {expectRequire} from 'a';
import proxyquire from 'proxyquire';
import sinon from 'sinon';

describe('Module Utils', () => {
  describe('extractModuleNames', () => {
    let utilsProxy;

    beforeEach(() => {
      // mock out path to avoid needing to use file system to find package.json
      let findUpStub, pathStub;

      findUpStub = {
        sync(path) {
          if (path === '.yo-rc.json') {
            return 'app/root/';
          }
        }
      };

      pathStub = {
        join() {
          return 'root/package.json';
        }
      };

      // proxy utils
      utilsProxy = proxyquire('../generators/utils/module', {
        'find-up': findUpStub,
        path: pathStub
      });

      expectRequire('root/package.json').return({name: 'test'});

      // mock function to prevent looking for build.config.js
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should return app name when using app', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('app')) === JSON.stringify(['test', null]));
    });

    it('should extract modules with slashes in path', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('test/parent/child')) === JSON.stringify(['child', 'parent']));
    });

    it('should return module without slashes in path', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('test')) === JSON.stringify(['test', null]));
    });
  });

  describe('moduleExists', () => {
    let pathStub, utilsProxy;

    beforeEach(() => {
      pathStub = {
        dirname() {
          return '.yo-rc.json';
        },
        join() {
          return 'app/home';
        }
      };
      utilsProxy = proxyquire('../generators/utils/module', {path: pathStub});

      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should return true when module is appDir', () => {
      assert(utilsProxy.moduleExists('app') === true);
    });

    it('should call fs.exstsSync', () => {
      const fsStub = {
        existsSync: sinon.stub().returns(true)
      };
      utilsProxy = proxyquire('../generators/utils/module', {fs: fsStub, path: pathStub});
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
      utilsProxy.normalizeModulePath = function normalizeModulePath() {
        return 'app/home';
      };
      utilsProxy.moduleExists('app/home');
      assert(fsStub.existsSync.callCount === 1);
    });
  });

  describe('moduleFilter', () => {
    let utilsProxy;

    beforeEach(() => {
      // mock out path to avoid needing to use file system to find package.json
      let pathStub;

      pathStub = {
        join() {
          return 'package.json';
        }
      };

      // proxy utils
      utilsProxy = proxyquire('../generators/utils/module', {path: pathStub});

      expectRequire('package.json').return({name: 'test'});

      // mock function to prevent looking for build.config.js
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should filter non-script files', () => {
      let expectedModules, files;

      files = [
        'test/test-module.html',
        'test1/test1-module.css',
        'test2/test2-module.js',
        'test3/test3-module.coffee',
        'test4/test4-module.es6',
        'test5/test5-module.ts'
      ];

      expectedModules = [
        {name: 'test2', value: 'test2'},
        {name: 'test3', value: 'test3'},
        {name: 'test4', value: 'test4'},
        {name: 'test5', value: 'test5'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should check for x-module name only on child directory', () => {
      let expectedModules, files;

      files = [
        'test/test-module.js',
        'abc/abc-module.js',
        'nested/nest/nest-module.js'
      ];

      expectedModules = [
        {name: 'test', value: 'test'},
        {name: 'abc', value: 'abc'},
        {name: 'nested/nest', value: 'nested/nest'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should remove duplicate directores', () => {
      let expectedModules, files;

      files = [
        'test1/test1-modulde.js',
        'test1/test1-module.ts',
        'test2/test2-module.js'
      ];

      expectedModules = [
        {name: 'test1', value: 'test1'},
        {name: 'test2', value: 'test2'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should only list directories that have a `directory`-module.{coffee,es6,js,ts} file', () => {
      let expectedModules, files;

      files = [
        'coffee-test/coffee-test-module.coffee',
        'es6-test/es6-test-module.es6',
        'js-test/js-test-module.js',
        'ts-test/ts-test-module.js',
        'components/gold-element/gold-element.js'
      ];

      expectedModules = [
        {name: 'coffee-test', value: 'coffee-test'},
        {name: 'es6-test', value: 'es6-test'},
        {name: 'js-test', value: 'js-test'},
        {name: 'ts-test', value: 'ts-test'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should strip Windows app path in value', () => {
      const files = ['app\\test\\test-module.js']
        , expectedModules = [{name: 'app\\test', value: 'test'}];

      let pathStub, windowsUtilsProxy;

      pathStub = {
        sep: '\\',
        basename() {
          return 'test-module.js';
        },
        dirname() {
          return 'app\\test';
        }
      };

      windowsUtilsProxy = proxyquire('../generators/utils/module', {path: pathStub});
      windowsUtilsProxy.getAppDir = () => {
        return 'app';
      };

      expect(windowsUtilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should strip Unix app path in value', () => {
      const files = ['app/test/test-module.js']
        , expectedModules = [{name: 'app/test', value: 'test'}];

      let pathStub, windowsUtilsProxy;

      pathStub = {
        sep: '/',
        basename() {
          return 'test-module';
        },
        dirname() {
          return 'app/test';
        }
      };

      windowsUtilsProxy = proxyquire('../generators/utils/module', {path: pathStub});
      windowsUtilsProxy.getAppDir = () => {
        return 'app';
      };

      expect(windowsUtilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });
  });
});
<commit_msg>test(utils-module): add verifications paths are being passed<commit_after>/* global describe, beforeEach, it */
'use strict';
import assert from 'assert';
import {expect} from 'chai';
import {expectRequire} from 'a';
import proxyquire from 'proxyquire';
import sinon from 'sinon';

describe('Module Utils', () => {
  describe('extractModuleNames', () => {
    let utilsProxy;

    beforeEach(() => {
      // mock out path to avoid needing to use file system to find package.json
      let findUpStub, pathStub;

      findUpStub = {
        sync(path) {
          if (path === '.yo-rc.json') {
            return 'app/root/.yo-rc.json';
          }
        }
      };

      pathStub = {
        join(appRoot, pkgPath) {
          if (appRoot === 'app/root' && pkgPath === 'package.json') {
            return 'app/root/package.json';
          }
        }
      };

      // proxy utils
      utilsProxy = proxyquire('../generators/utils/module', {
        'find-up': findUpStub,
        path: pathStub
      });

      expectRequire('app/root/package.json').return({name: 'test'});

      // mock function to prevent looking for build.config.js
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should return app name when using app', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('app')) === JSON.stringify(['test', null]));
    });

    it('should extract modules with slashes in path', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('test/parent/child')) === JSON.stringify(['child', 'parent']));
    });

    it('should return module without slashes in path', () => {
      assert(JSON.stringify(utilsProxy.extractModuleNames('test')) === JSON.stringify(['test', null]));
    });
  });

  describe('moduleExists', () => {
    let pathStub, utilsProxy;

    beforeEach(() => {
      pathStub = {
        dirname() {
          return '.yo-rc.json';
        },
        join() {
          return 'app/home';
        }
      };
      utilsProxy = proxyquire('../generators/utils/module', {path: pathStub});

      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should return true when module is appDir', () => {
      assert(utilsProxy.moduleExists('app') === true);
    });

    it('should call fs.exstsSync', () => {
      const fsStub = {
        existsSync: sinon.stub().returns(true)
      };
      utilsProxy = proxyquire('../generators/utils/module', {fs: fsStub, path: pathStub});
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
      utilsProxy.normalizeModulePath = function normalizeModulePath() {
        return 'app/home';
      };
      utilsProxy.moduleExists('app/home');
      assert(fsStub.existsSync.callCount === 1);
    });
  });

  describe('moduleFilter', () => {
    let utilsProxy;

    beforeEach(() => {
      // mock out path to avoid needing to use file system to find package.json
      let pathStub;

      pathStub = {
        join() {
          return 'package.json';
        }
      };

      // proxy utils
      utilsProxy = proxyquire('../generators/utils/module', {path: pathStub});

      expectRequire('package.json').return({name: 'test'});

      // mock function to prevent looking for build.config.js
      utilsProxy.getAppDir = function getAppDir() {
        return 'app';
      };
    });

    it('should filter non-script files', () => {
      let expectedModules, files;

      files = [
        'test/test-module.html',
        'test1/test1-module.css',
        'test2/test2-module.js',
        'test3/test3-module.coffee',
        'test4/test4-module.es6',
        'test5/test5-module.ts'
      ];

      expectedModules = [
        {name: 'test2', value: 'test2'},
        {name: 'test3', value: 'test3'},
        {name: 'test4', value: 'test4'},
        {name: 'test5', value: 'test5'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should check for x-module name only on child directory', () => {
      let expectedModules, files;

      files = [
        'test/test-module.js',
        'abc/abc-module.js',
        'nested/nest/nest-module.js'
      ];

      expectedModules = [
        {name: 'test', value: 'test'},
        {name: 'abc', value: 'abc'},
        {name: 'nested/nest', value: 'nested/nest'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should remove duplicate directores', () => {
      let expectedModules, files;

      files = [
        'test1/test1-modulde.js',
        'test1/test1-module.ts',
        'test2/test2-module.js'
      ];

      expectedModules = [
        {name: 'test1', value: 'test1'},
        {name: 'test2', value: 'test2'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should only list directories that have a `directory`-module.{coffee,es6,js,ts} file', () => {
      let expectedModules, files;

      files = [
        'coffee-test/coffee-test-module.coffee',
        'es6-test/es6-test-module.es6',
        'js-test/js-test-module.js',
        'ts-test/ts-test-module.js',
        'components/gold-element/gold-element.js'
      ];

      expectedModules = [
        {name: 'coffee-test', value: 'coffee-test'},
        {name: 'es6-test', value: 'es6-test'},
        {name: 'js-test', value: 'js-test'},
        {name: 'ts-test', value: 'ts-test'}
      ];

      expect(utilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should strip Windows app path in value', () => {
      const files = ['app\\test\\test-module.js']
        , expectedModules = [{name: 'app\\test', value: 'test'}];

      let pathStub, windowsUtilsProxy;

      pathStub = {
        sep: '\\',
        basename() {
          return 'test-module.js';
        },
        dirname() {
          return 'app\\test';
        }
      };

      windowsUtilsProxy = proxyquire('../generators/utils/module', {path: pathStub});
      windowsUtilsProxy.getAppDir = () => {
        return 'app';
      };

      expect(windowsUtilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });

    it('should strip Unix app path in value', () => {
      const files = ['app/test/test-module.js']
        , expectedModules = [{name: 'app/test', value: 'test'}];

      let pathStub, windowsUtilsProxy;

      pathStub = {
        sep: '/',
        basename() {
          return 'test-module';
        },
        dirname() {
          return 'app/test';
        }
      };

      windowsUtilsProxy = proxyquire('../generators/utils/module', {path: pathStub});
      windowsUtilsProxy.getAppDir = () => {
        return 'app';
      };

      expect(windowsUtilsProxy.moduleFilter(files)).to.eql(expectedModules);
    });
  });
});
<|endoftext|>


<commit_before>{
	"translatorID":"d0b1914a-11f1-4dd7-8557-b32fe8a3dd47",
	"translatorType":4,
	"label":"EBSCOhost",
	"creator":"Simon Kornblith and Michael Berkowitz",
	"target":"https?://[^/]+/(?:bsi|ehost)/(?:results|detail|folder)",
	"minVersion":"1.0.0b3.r1",
	"maxVersion":"",
	"priority":100,
	"inRepository":true,
	"lastUpdated":"2009-01-05 21:20:00"
}

function detectWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	// See if this is a search results or folder results page
	var searchResult = doc.evaluate('//ul[@class="result-list" or @class="folder-list"]/li/div[@class="result-list-record" or @class="folder-item"]', doc, nsResolver,
	                                XPathResult.ANY_TYPE, null).iterateNext();
	if(searchResult) {
		return "multiple";
	}
/*
	var xpath = '//div[@class="citation-wrapping-div"]/dl[@class="citation-fields"]/dt[starts-with(text(), "Persistent link to this record")'
		+' or starts-with(text(), "Vínculo persistente a este informe")'
		+' or starts-with(text(), "Lien permanent à cette donnée")'
		+' or starts-with(text(), "Permanenter Link zu diesem Datensatz")'
		+' or starts-with(text(), "Link permanente al record")'
		+' or starts-with(text(), "Link permanente para este registro")'
		+' or starts-with(text(), "本記錄固定連結")'
		+' or starts-with(text(), "此记录的永久链接")'
		+' or starts-with(text(), "このレコードへのパーシスタント リンク")'
		+' or starts-with(text(), "레코드 링크 URL")'
		+' or starts-with(text(), "Постоянная ссылка на эту запись")'
		+' or starts-with(text(), "Bu kayda sürekli bağlantı")'
		+' or starts-with(text(), "Μόνιμος σύνδεσμος σε αυτό το αρχείο")]';
*/
	var xpath = '//input[@id="ctl00_ctl00_MainContentArea_MainContentArea_topDeliveryControl_deliveryButtonControl_lnkExportImage"]';
	var persistentLink = doc.evaluate(xpath, doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
	if(persistentLink) {
		return "journalArticle";
	}
}

var customViewStateMatch = /<input type="hidden" name="__CUSTOMVIEWSTATE" id="__CUSTOMVIEWSTATE" value="([^"]+)" \/>/
var host;

function fullEscape(text) {
	return escape(text).replace(/\//g, "%2F").replace(/\+/g, "%2B");
}

function generateDeliverString(nsResolver, doc){
	var hiddenInputs = doc.evaluate('//input[@type="hidden" and not(contains(@name, "folderHas"))]', doc, nsResolver, XPathResult.ANY_TYPE, null);
	var hiddenInput;
	var deliverString ="";
	while(hiddenInput = hiddenInputs.iterateNext()) {
		deliverString = deliverString+hiddenInput.name.replace(/\$/g, "%24")+"="+encodeURIComponent(hiddenInput.value) + "&";
	}
	var otherHiddenInputs = doc.evaluate('//input[@type="hidden" and contains(@name, "folderHas")]', doc, nsResolver, XPathResult.ANY_TYPE, null);
	while(hiddenInput = otherHiddenInputs.iterateNext()) {
		deliverString = deliverString+hiddenInput.name.replace(/\$/g, "%24")+"="+escape(hiddenInput.value).replace(/\//g, "%2F").replace(/%20/g, "+") + "&";
	}

	deliverString = deliverString
		+"&ctl00%24ctl00%24MainContentArea%24MainContentArea%24topDeliveryControl%24deliveryButtonControl%24lnkExportImage.x=5"
		+"&ctl00%24ctl00%24MainContentArea%24MainContentArea%24topDeliveryControl%24deliveryButtonControl%24lnkExportImage.y=14";

	return deliverString;
}

/*
 * given the text of the delivery page, downloads an item
 */
function downloadFunction(text) {
	var postLocation = /<form (?:autocomplete="o(?:ff|n)" )?name="aspnetForm" method="post" action="([^"]+)"/
	var m = postLocation.exec(text);
	var deliveryURL = m[1].replace(/&/g, "&");
	m = customViewStateMatch.exec(text);
	var downloadString = "__EVENTTARGET=&__EVENTARGUMENT=&__CUSTOMVIEWSTATE="+fullEscape(m[1])+"&__VIEWSTATE=&ctl00%24ctl00%24MainContentArea%24MainContentArea%24ctl00%24btnSubmit=Save&ctl00%24ctl00%24MainContentArea%24MainContentArea%24ctl00%24BibFormat=1&ajax=enabled";

	Zotero.Utilities.HTTP.doPost(host+"/ehost/"+deliveryURL,
								 downloadString, function(text) {	// get marked records as RIS
		// load translator for RIS
		var test = text.match(/UR\s+\-(.*)/g);
		if (test[0].match("@")) text = text.replace(/UR\s+\-(.*)/, "");
		if (text.match(/AB\s\s\-/)) text = text.replace(/AB\s\s\-/, "N2  -");
		var translator = Zotero.loadTranslator("import");
		translator.setTranslator("32d59d2d-b65a-4da4-b0a3-bdd3cfb979e7");
		translator.setString(text);
		translator.setHandler("itemDone", function(obj, item) {
			if (text.match("L3")) {
				item.DOI = text.match(/L3\s+\-\s*(.*)/)[1];
			}
			item.itemType = "journalArticle";
			item.complete();
		});
		translator.translate();

		Zotero.done();
	});
}

function doWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var hostRe = new RegExp("^(https?://[^/]+)/");
	var m = hostRe.exec(url);
	host = m[1];

	var searchResult = doc.evaluate('//ul[@class="result-list" or @class="folder-list"]/li/div[@class="result-list-record" or @class="folder-item"]', doc, nsResolver,
	                                XPathResult.ANY_TYPE, null).iterateNext();

	if(searchResult) {
		var titlex = '//div[@class="result-list-record" or @class="folder-item-detail"]/span/a';
		var titles = doc.evaluate(titlex, doc, nsResolver, XPathResult.ANY_TYPE, null);
		var items = new Object();
		var title;
		while (title = titles.iterateNext()) {
			items[title.href] = title.textContent;
		}

		var items = Zotero.selectItems(items);
		if(!items) {
			return true;
		}

		var uris = new Array();
		for(var i in items) {
			uris.push(i);
		}

		Zotero.Utilities.processDocuments(uris, function(newDoc){
			var postURL = newDoc.evaluate('//form[@name="aspnetForm"]/@action', newDoc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
			postURL = host+"/ehost/"+postURL.nodeValue;
			var deliverString = generateDeliverString(nsResolver, newDoc);
			Zotero.Utilities.HTTP.doPost(postURL, deliverString, downloadFunction);
		});
	} else {
		var postURL = doc.evaluate('//form[@name="aspnetForm"]/@action', doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
		postURL = host+"/ehost/"+postURL.nodeValue;
		var deliverString = generateDeliverString(nsResolver, doc);
		Zotero.Utilities.HTTP.doPost(postURL, deliverString, downloadFunction);
	}
	Zotero.wait();
}<commit_msg>quick fix for EBSCOhost, no PDF support and there are async issues...but it works<commit_after>{
	"translatorID":"d0b1914a-11f1-4dd7-8557-b32fe8a3dd47",
	"translatorType":4,
	"label":"EBSCOhost",
	"creator":"Simon Kornblith and Michael Berkowitz",
	"target":"https?://[^/]+/(?:bsi|ehost)/(?:results|detail|folder)",
	"minVersion":"1.0.0b3.r1",
	"maxVersion":"",
	"priority":100,
	"inRepository":true,
	"lastUpdated":"2009-01-05 21:20:00"
}

function detectWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	// See if this is a search results or folder results page
	var searchResult = doc.evaluate('//ul[@class="result-list" or @class="folder-list"]/li/div[@class="result-list-record" or @class="folder-item"]', doc, nsResolver,
	                                XPathResult.ANY_TYPE, null).iterateNext();
	if(searchResult) {
		return "multiple";
	}
/*
	var xpath = '//div[@class="citation-wrapping-div"]/dl[@class="citation-fields"]/dt[starts-with(text(), "Persistent link to this record")'
		+' or starts-with(text(), "Vínculo persistente a este informe")'
		+' or starts-with(text(), "Lien permanent à cette donnée")'
		+' or starts-with(text(), "Permanenter Link zu diesem Datensatz")'
		+' or starts-with(text(), "Link permanente al record")'
		+' or starts-with(text(), "Link permanente para este registro")'
		+' or starts-with(text(), "本記錄固定連結")'
		+' or starts-with(text(), "此记录的永久链接")'
		+' or starts-with(text(), "このレコードへのパーシスタント リンク")'
		+' or starts-with(text(), "레코드 링크 URL")'
		+' or starts-with(text(), "Постоянная ссылка на эту запись")'
		+' or starts-with(text(), "Bu kayda sürekli bağlantı")'
		+' or starts-with(text(), "Μόνιμος σύνδεσμος σε αυτό το αρχείο")]';
*/
	var xpath = '//input[@id="ctl00_ctl00_MainContentArea_MainContentArea_topDeliveryControl_deliveryButtonControl_lnkExportImage"]';
	var persistentLink = doc.evaluate(xpath, doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
	if(persistentLink) {
		return "journalArticle";
	}
}

var customViewStateMatch = /<input type="hidden" name="__CUSTOMVIEWSTATE" id="__CUSTOMVIEWSTATE" value="([^"]+)" \/>/
var host;

function fullEscape(text) {
	return escape(text).replace(/\//g, "%2F").replace(/\+/g, "%2B");
}

function generateDeliverString(nsResolver, doc){
	var hiddenInputs = doc.evaluate('//input[@type="hidden" and not(contains(@name, "folderHas")) and not(@name ="ajax")]', doc, nsResolver, XPathResult.ANY_TYPE, null);
	var hiddenInput;
	var deliverString ="";
	while(hiddenInput = hiddenInputs.iterateNext()) {
		deliverString = deliverString+hiddenInput.name.replace(/\$/g, "%24")+"="+encodeURIComponent(hiddenInput.value) + "&";
	}
	var otherHiddenInputs = doc.evaluate('//input[@type="hidden" and contains(@name, "folderHas")]', doc, nsResolver, XPathResult.ANY_TYPE, null);
	while(hiddenInput = otherHiddenInputs.iterateNext()) {
		deliverString = deliverString+hiddenInput.name.replace(/\$/g, "%24")+"="+escape(hiddenInput.value).replace(/\//g, "%2F").replace(/%20/g, "+") + "&";
	}

	deliverString = deliverString
		+"&ctl00%24ctl00%24MainContentArea%24MainContentArea%24topDeliveryControl%24deliveryButtonControl%24lnkExportImage.x=5"
		+"&ctl00%24ctl00%24MainContentArea%24MainContentArea%24topDeliveryControl%24deliveryButtonControl%24lnkExportImage.y=14";

	return deliverString;
}

/*
 * given the text of the delivery page, downloads an item
 */
function downloadFunction(text) {
	Zotero.debug("POSTTEXT="+text);
	var postLocation = /<form (?:autocomplete="o(?:ff|n)" )?name="aspnetForm" method="post" action="([^"]+)"/
	var postMatch = postLocation.exec(text);
	var deliveryURL = postMatch[1].replace(/&/g, "&");
	postMatch = customViewStateMatch.exec(text);
	var downloadString = "__EVENTTARGET=&__EVENTARGUMENT=&__CUSTOMVIEWSTATE="+fullEscape(postMatch[1])+"&__VIEWSTATE=&ctl00%24ctl00%24MainContentArea%24MainContentArea%24ctl00%24btnSubmit=Save&ctl00%24ctl00%24MainContentArea%24MainContentArea%24ctl00%24BibFormat=1&ajax=enabled";

	Zotero.Utilities.HTTP.doPost(host+"/ehost/"+deliveryURL,
								 downloadString, function(text) {	// get marked records as RIS
		// load translator for RIS
		var test = text.match(/UR\s+\-(.*)/g);
		if (test[0].match("@")) text = text.replace(/UR\s+\-(.*)/, "");
		if (text.match(/AB\s\s\-/)) text = text.replace(/AB\s\s\-/, "N2  -");
		var translator = Zotero.loadTranslator("import");
		translator.setTranslator("32d59d2d-b65a-4da4-b0a3-bdd3cfb979e7");
		translator.setString(text);
		translator.setHandler("itemDone", function(obj, item) {
			if (text.match("L3")) {
				item.DOI = text.match(/L3\s+\-\s*(.*)/)[1];
			}
			item.itemType = "journalArticle";
			item.complete();
		});
		translator.translate();

		Zotero.done();
	});
}

function doWeb(doc, url) {
	var namespace = doc.documentElement.namespaceURI;
	var nsResolver = namespace ? function(prefix) {
		if (prefix == 'x') return namespace; else return null;
	} : null;

	var hostRe = new RegExp("^(https?://[^/]+)/");
	var hostMatch = hostRe.exec(url);
	host = hostMatch[1];

	var searchResult = doc.evaluate('//ul[@class="result-list" or @class="folder-list"]/li/div[@class="result-list-record" or @class="folder-item"]', doc, nsResolver,
	                                XPathResult.ANY_TYPE, null).iterateNext();

	if(searchResult) {
		var titlex = '//div[@class="result-list-record" or @class="folder-item-detail"]/span/a';
		var titles = doc.evaluate(titlex, doc, nsResolver, XPathResult.ANY_TYPE, null);
		var items = new Object();
		var title;
		while (title = titles.iterateNext()) {
			items[title.href] = title.textContent;
		}

		var items = Zotero.selectItems(items);
		if(!items) {
			return true;
		}

		var uris = new Array();
		for(var i in items) {
			uris.push(i);
		}

		Zotero.Utilities.processDocuments(uris, function(newDoc){
			var postURL = newDoc.evaluate('//form[@name="aspnetForm"]/@action', newDoc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
			postURL = host+"/ehost/"+postURL.nodeValue;
			var deliverString = generateDeliverString(nsResolver, newDoc);
			Zotero.Utilities.HTTP.doPost(postURL, deliverString, downloadFunction);
		});
	} else {
		//This is a hack, generateDeliveryString is acting up for single pages, but it works on the plink url
		var link = [doc.evaluate("//input[@id ='pLink']/@value", doc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext().nodeValue];
		Zotero.Utilities.processDocuments(link, function(newDoc){
			var postURL = newDoc.evaluate('//form[@name="aspnetForm"]/@action', newDoc, nsResolver, XPathResult.ANY_TYPE, null).iterateNext();
			postURL = host+"/ehost/"+postURL.nodeValue;
			var deliverString = generateDeliverString(nsResolver, newDoc);
			Zotero.Utilities.HTTP.doPost(postURL, deliverString, downloadFunction);
		});

	}
	Zotero.wait();
}<|endoftext|>


<commit_before>var fs = require('fs');
var coopy = require('coopy');
var jtable = require('jtable');
var tester = require('tester');

{
    var t1 = new jtable.JTable2([["Name","Number"],["John",14],["Jane",99]]);
    var t2 = new jtable.JTable2([["Name","Number"],["Mary",17],["John",14],["Jane",99]]);
    var t3 = new jtable.JTable2([["Name","Number"],["John",15],["Sam",21],["Jane",99]]);
    var t4 = new jtable.JTable2([["Name","Number"],["John",15],["Nimble",88],["Sam",21],["Jane",99]]);
    var t5 = new jtable.JTable2([["Name","Number","Planet"],["John",14,"Earth"],["Jane",99,"Mercury"]]);
    var t6 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Earth"],["Jane","Mercury"]]);
    var t7 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Ea->rth"],["Jane","Mercury"]]);
    var t8 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John",null],["Jane","Mercury"]]);
    var t9 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","NULL"],["Jane","Mercury"]]);
    var t10 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","_NULL"],["Jane","Mercury"]]);
    var t11 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Pluto but it is not\na planet anymore"],["Jane","Mercury"]]);
    var t12 = new jtable.JTable2([["Planet"],["Jupiter"],["Pluto but it is not\na planet anymore"],["Mercury"]]);

    var txt = fs.readFileSync("data/quote_me.csv","utf8");
    var quote_me = new jtable.JTable2((new coopy.Csv()).parseTable(txt));
    txt = fs.readFileSync("data/quote_me2.csv","utf8");
    var quote_me2 = new jtable.JTable2((new coopy.Csv()).parseTable(txt));
    txt = fs.readFileSync("data/bridges.csv","utf8");
    var bridges = new jtable.JTable2((new coopy.Csv()).parseTable(txt));

    {
	var ct = new coopy.Coopy.compareTables3(t1,t2,t3);
	var align = ct.align();
	tester.align_asserts(align,
			     [[0,0],[1,1],[2,3]]);
	tester.align_asserts(align.reference,
			     [[0,0],[1,2],[2,3]]);
	tester.order_asserts(align.toOrder(),
			     [[0,0,0],
			      [1,-1,-1],
			      [2,1,1],
			      [-1,2,-1],
			      [3,3,2]]);

	var options = new coopy.CompareFlags();
	var td = new coopy.TableDiff(align,options);
	var output = new jtable.JTable2([]);
	td.hilite(output);
    }

    var tables = [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12];
    var names = ["t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10", "t11", "t12"];
    for (var i in tables) {
	for (var j in tables) {
	    tester.round_trip(tables[i],tables[j],names[i] + " -> " + names[j]);
	}
    }

    tables = [quote_me, quote_me2];
    names = ["quote_me", "quote_me2"];
    for (var i in tables) {
	for (var j in tables) {
	    tester.round_trip(tables[i],tables[j],names[i] + " -> " + names[j]);
	}
    }

    tables = [bridges];
    names = ["bridges"];
    for (var i=0; i<bridges.get_width(); i++) {
	var t = bridges.clone();
	new coopy.TableModifier(t).removeColumn(i);
	tables.push(t);
	names.push("bridges_less_column_" + i);
    }
    for (var i in tables) {
	for (var j in tables) {
	    tester.round_trip(tables[i],tables[j],names[i] + " -> " + names[j]);
	}
    }

    var bridges_col0 = bridges.clone();
    bridges_col0.setCell(0,0,"bridger");
    tester.round_trip(bridges,bridges_col0,"change column name");
}
<commit_msg>many-to-many highlighter roundtrip tests<commit_after>var fs = require('fs');
var coopy = require('coopy');
var jtable = require('jtable');
var tester = require('tester');

{
    var t1 = new jtable.JTable2([["Name","Number"],["John",14],["Jane",99]]);
    var t2 = new jtable.JTable2([["Name","Number"],["Mary",17],["John",14],["Jane",99]]);
    var t3 = new jtable.JTable2([["Name","Number"],["John",15],["Sam",21],["Jane",99]]);
    var t4 = new jtable.JTable2([["Name","Number"],["John",15],["Nimble",88],["Sam",21],["Jane",99]]);
    var t5 = new jtable.JTable2([["Name","Number","Planet"],["John",14,"Earth"],["Jane",99,"Mercury"]]);
    var t6 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Earth"],["Jane","Mercury"]]);
    var t7 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Ea->rth"],["Jane","Mercury"]]);
    var t8 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John",null],["Jane","Mercury"]]);
    var t9 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","NULL"],["Jane","Mercury"]]);
    var t10 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","_NULL"],["Jane","Mercury"]]);
    var t11 = new jtable.JTable2([["Name","Planet"],["Frank","Jupiter"],["John","Pluto but it is not\na planet anymore"],["Jane","Mercury"]]);
    var t12 = new jtable.JTable2([["Planet"],["Jupiter"],["Pluto but it is not\na planet anymore"],["Mercury"]]);

    var txt = fs.readFileSync("data/quote_me.csv","utf8");
    var quote_me = new jtable.JTable2((new coopy.Csv()).parseTable(txt));
    txt = fs.readFileSync("data/quote_me2.csv","utf8");
    var quote_me2 = new jtable.JTable2((new coopy.Csv()).parseTable(txt));
    txt = fs.readFileSync("data/bridges.csv","utf8");
    var bridges = new jtable.JTable2((new coopy.Csv()).parseTable(txt));

    {
	var ct = new coopy.Coopy.compareTables3(t1,t2,t3);
	var align = ct.align();
	tester.align_asserts(align,
			     [[0,0],[1,1],[2,3]]);
	tester.align_asserts(align.reference,
			     [[0,0],[1,2],[2,3]]);
	tester.order_asserts(align.toOrder(),
			     [[0,0,0],
			      [1,-1,-1],
			      [2,1,1],
			      [-1,2,-1],
			      [3,3,2]]);

	var options = new coopy.CompareFlags();
	var td = new coopy.TableDiff(align,options);
	var output = new jtable.JTable2([]);
	td.hilite(output);
    }

    var tables = [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, quote_me, quote_me2, bridges];
    var names = ["t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10", "t11", "t12", "quote_me", "quote_me2", "bridges"];
    for (var i=0; i<bridges.get_width(); i++) {
	var t = bridges.clone();
	new coopy.TableModifier(t).removeColumn(i);
	tables.push(t);
	names.push("bridges_less_column_" + i);
    }
    var bridges_col0 = bridges.clone();
    bridges_col0.setCell(0,0,"bridger");
    tables.push(bridges_col0);
    names.push("bridges_rename_column_0");
    for (var i in tables) {
	for (var j in tables) {
	    tester.round_trip(tables[i],tables[j],names[i] + " -> " + names[j]);
	}
    }
}
<|endoftext|>


<commit_before>var tokenCreator = require('../tokenCreator.js');
var assert = require('assert');

describe('makeToken', function () {
	it('creates a token using aes-cmac', function () {
		var key = new Buffer('6b334d656e2a702f322e336a34616242', 'hex');
		var data = ['user123', '2015-03-13T05:30:00Z'];
		var expected = 'user123|2015-03-13T05:30:00Z|17ea924623dd6eef23d2faeb3ccd293e';
		assert.equal(tokenCreator.createToken(key, data), expected);
	});
});
<commit_msg>Changed the describe string.<commit_after>var tokenCreator = require('../tokenCreator.js');
var assert = require('assert');

describe('tokenCreator', function () {
	it('creates a token using aes-cmac', function () {
		var key = new Buffer('6b334d656e2a702f322e336a34616242', 'hex');
		var data = ['user123', '2015-03-13T05:30:00Z'];
		var expected = 'user123|2015-03-13T05:30:00Z|17ea924623dd6eef23d2faeb3ccd293e';
		assert.equal(tokenCreator.createToken(key, data), expected);
	});
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTable from './components/propTable';
import { storiesOf } from '@storybook/react';
import { checkA11y } from 'storybook-addon-a11y';
import { withKnobs, boolean, number, select } from '@storybook/addon-knobs/react';
import { withInfo } from '@storybook/addon-info';
import { DataGrid, Heading4, IconMenu, MenuItem, Link, StatusBullet, TextSmall, Tooltip, Button } from '../components';
import { rows1, rows2 } from '../static/data/datagrid';

const TooltippedStatusBullet = Tooltip(StatusBullet);

const handleRowSelectionChange = selectedRows => {
  console.log(`onSelectionChange - selected row indexes: ${selectedRows}`);
};

storiesOf('DataGrids', module)
  .addDecorator((story, context) =>
    withInfo({
      TableComponent: PropTable,
      propTablesExclude: [Link, TextSmall, Heading4, IconMenu, MenuItem],
    })(story)(context),
  )
  .addDecorator(checkA11y)
  .addDecorator(withKnobs)
  .add('Basic', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
      checkboxSize={select('Checkbox size', ['small', 'medium', 'large'], 'small')}
    >
      <DataGrid.HeaderRowOverlay>
        <Button label="Book" />
        <Button label="Mark as paid" />
      </DataGrid.HeaderRowOverlay>
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
    </DataGrid>
  ))
  .add('with footer', () => (
    <DataGrid selectable={boolean('Selectable', true)} comparableId={1} onSelectionChange={handleRowSelectionChange}>
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows2.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell align="right">{`${rows1.length} rows in total`}</DataGrid.Cell>
      </DataGrid.FooterRow>
    </DataGrid>
  ))
  .add('with sticky columns', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      stickyFromLeft={number('Sticky from left', 3)}
      stickyFromRight={number('Sticky from right', 1)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
    >
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
    </DataGrid>
  ))
  .add('Advanced', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      stickyFromLeft={number('Sticky from left', 3)}
      stickyFromRight={number('Sticky from right', 1)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
    >
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>Total Excl Btw</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong>
          € 13460.52
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>

      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>+ VAT</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong>
          € 2826.71
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>

      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>Total Incl Btw</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong backgroundColor="white" border="around">
          € 16287.23
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>
    </DataGrid>
  ));
<commit_msg>Update story to reflect ButtonGroup removal<commit_after>import React from 'react';
import PropTable from './components/propTable';
import { storiesOf } from '@storybook/react';
import { checkA11y } from 'storybook-addon-a11y';
import { withKnobs, boolean, number, select } from '@storybook/addon-knobs/react';
import { withInfo } from '@storybook/addon-info';
import {
  DataGrid,
  Heading4,
  IconMenu,
  MenuItem,
  Link,
  StatusBullet,
  TextSmall,
  Tooltip,
  Button,
  ButtonGroup,
} from '../components';
import { rows1, rows2 } from '../static/data/datagrid';

const TooltippedStatusBullet = Tooltip(StatusBullet);

const handleRowSelectionChange = selectedRows => {
  console.log(`onSelectionChange - selected row indexes: ${selectedRows}`);
};

storiesOf('DataGrids', module)
  .addDecorator((story, context) =>
    withInfo({
      TableComponent: PropTable,
      propTablesExclude: [Link, TextSmall, Heading4, IconMenu, MenuItem],
    })(story)(context),
  )
  .addDecorator(checkA11y)
  .addDecorator(withKnobs)
  .add('Basic', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
      checkboxSize={select('Checkbox size', ['small', 'medium', 'large'], 'small')}
    >
      <DataGrid.HeaderRowOverlay>
        <ButtonGroup segmented marginRight={3}>
          <Button label="Book" />
          <Button label="Mark as paid" />
        </ButtonGroup>
        <Button level="destructive" label="Delete" />
      </DataGrid.HeaderRowOverlay>
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
    </DataGrid>
  ))
  .add('with footer', () => (
    <DataGrid selectable={boolean('Selectable', true)} comparableId={1} onSelectionChange={handleRowSelectionChange}>
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows2.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell align="right">{`${rows1.length} rows in total`}</DataGrid.Cell>
      </DataGrid.FooterRow>
    </DataGrid>
  ))
  .add('with sticky columns', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      stickyFromLeft={number('Sticky from left', 3)}
      stickyFromRight={number('Sticky from right', 1)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
    >
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
    </DataGrid>
  ))
  .add('Advanced', () => (
    <DataGrid
      selectable={boolean('Selectable', true)}
      stickyFromLeft={number('Sticky from left', 3)}
      stickyFromRight={number('Sticky from right', 1)}
      comparableId={1}
      onSelectionChange={handleRowSelectionChange}
    >
      <DataGrid.HeaderRow>
        <DataGrid.HeaderCell flex="min-width" />
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} sorted="asc">
          Invoice
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')} align="right">
          Amount
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="2" onClick={() => console.log('onClick: column sort')}>
          Customer
        </DataGrid.HeaderCell>
        <DataGrid.HeaderCell onClick={() => console.log('onClick: column sort')}>Due date</DataGrid.HeaderCell>
        <DataGrid.HeaderCell flex="min-width" />
      </DataGrid.HeaderRow>
      {rows1.map((row, index) => {
        return (
          <DataGrid.BodyRow key={index}>
            <DataGrid.Cell align="center" flex="min-width">
              <TooltippedStatusBullet
                color={row.column1}
                tooltip={<TextSmall>Overdue</TextSmall>}
                tooltipColor={row.column1}
                tooltipSize="small"
                size="medium"
              />
            </DataGrid.Cell>
            <DataGrid.Cell>
              <Link href="#" inherit={false}>
                {row.column5}
              </Link>{' '}
            </DataGrid.Cell>
            <DataGrid.Cell align="right" strong>
              {' '}
              {`€ ${row.column3}`}
            </DataGrid.Cell>
            <DataGrid.Cell flex="2">{row.column2}</DataGrid.Cell>
            <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
            <DataGrid.Cell align="right" flex="min-width" preventOverflow={false}>
              <IconMenu position="top-right">
                <MenuItem onClick={() => console.log('onClick: delete row')}>Remove row</MenuItem>
              </IconMenu>
            </DataGrid.Cell>
          </DataGrid.BodyRow>
        );
      })}
      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>Total Excl Btw</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong>
          € 13460.52
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>

      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>+ VAT</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong>
          € 2826.71
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>

      <DataGrid.FooterRow backgroundColor="neutral">
        <DataGrid.Cell flex="min-width" />
        <DataGrid.Cell align="right" soft>
          <Heading4>Total Incl Btw</Heading4>
        </DataGrid.Cell>
        <DataGrid.Cell align="right" strong backgroundColor="white" border="around">
          € 16287.23
        </DataGrid.Cell>
        <DataGrid.Cell flex="2" />
        <DataGrid.Cell />
        <DataGrid.Cell flex="min-width" />
      </DataGrid.FooterRow>
    </DataGrid>
  ));
<|endoftext|>


<commit_before>import { _ } from 'lodash';
import fakeFetch from 'fake-fetch';
import { Container } from 'aurelia-dependency-injection';
import { StageComponent } from 'aurelia-testing';
import { bootstrap } from 'aurelia-bootstrapper';

import { Config } from '../../src/config';
import { Service } from '../../src/service';

describe('Service', () => {
  let component;
  let container;
  let config;

  beforeAll(done => {
    fakeFetch.install();
    component = StageComponent.withResources();
    component.create(bootstrap)
      .then(() => {
        container = Container.instance;
        config = container.get(Config);
        done();
      });
  });

  afterAll(() => {
    fakeFetch.restore();
    component.dispose();
  });

  describe('.configure()', () => {
    it('Should properly initialize the service', () => {
      let service = new Service();
      service.configure('myKey');

      expect(service.container).toBe(container);
      expect(service.modelid).toBe('_id');
      expect(service.modelClass).toBeUndefined();
      expect(service.defaultRoute).toBe('/api/myKey/');
      expect(service.collection instanceof Array).toBe(true);
      expect(service.refKeys()).toEqual([]);
      expect(service.isComplete()).toBeTruthy();
    });
  });

  describe('.fromJSON', () => {
    let service;

    beforeEach(() => {
      let creator = (data) => data;
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return null when data is undefined or null', () => {
      service.fromJSON().then(res => {
        expect(res).toEqual(null);
      });
    });

    it('Should return a new model when data has an id not found in the collection', () => {
      service.fromJSON({ _id: 'myId', foo: 21 }).then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 21 });
      });
    });

    it('Should return a existing model, _syncFrom depending on isComplete, if data id is found in the collection', () => {
      let model = {
        _id: 'myId',
        foo: 42
      };
      service.collection.push(model);

      service.fromJSON({ _id: 'myId' }).then(res => {
        expect(res).toEqual(model);
      });
    });
  });

  describe('.toJSON()', () => {
    let model;
    let service;

    beforeEach(() => {
      let creator = (data) => data;
      model = { _id: 'fakeId', other: 'field' };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return the model when the model doesn\'t have a toJSON method', () => {
      let result = service.toJSON(model);
      expect(result).toEqual(model);
    });

    it('Should call the toJSON method of a model', () => {
      model.toJSON = () => 'myFakeJSON';

      let result = service.toJSON(model);
      expect(result).toBe('myFakeJSON');
    });
  });

  describe('.flush()', () => {
    it('Should clear the collection from the created models', done => {
      fakeFetch.respondWith('{ "_id": "myId" }');

      let service = new Service();
      config.registerService('myKey', service);

      expect(service.collection).toEqual([]);

      service.get('myId')
        .then(res => {
          expect(res._id).toBe('myId');
          service.flush();
          expect(service.collection).toEqual([]);
          done();
        });
    });
  });

  describe('.isComplete', () => {
    it('Should return a boolean', () => {
      let service = new Service();
      expect(service.isComplete({})).toBeTruthy();
    });
  });

  describe('.sync', () => {
    let model;
    let service;
    beforeEach(() => {
      fakeFetch.respondWith('{ "_id": "myId", "foo": "bar" }');
      let creator = (data) => data;
      model = { _id: 'myId' };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return the synced model when given an id', done => {
      service.collection.push(model);
      service.sync('myId').then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 'bar' });
        done();
      });
    });

    it('Should return the synced model when given json data of the model', done => {
      service.collection.push(model);
      service.sync({ _id: 'myId' }).then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 'bar' });
        done();
      });
    });
  });

  describe('.refKeys', () => {
    it('Should return an empty Array', () => {
      let service = new Service();
      expect(service.refKeys()).toEqual([]);
    });
  });

  describe('._setHttpClient', () => {
    it('Should return the same httpClient object', () => {
      let service = new Service();
      service._setHttpClient({ dummy: true });
      expect(service._httpClient).toEqual({ dummy: true });
    });
  });

  describe('._syncFrom', () => {
    it('Should merge two literal objects', () => {
      let foo = { f1: 1 };
      let bar = { f2: 2 };
      let service = new Service();

      service._syncFrom(foo, bar);
      expect(foo).toEqual({ f1: 1, f2: 2 });
    });
  });

  describe('._getFromCollection', () => {
    it('Should find a model with default _id property value', () => {
      let creator = (data) => data;
      let model = { _id: 'myId' };
      let service = new Service();
      config.registerService('myKey', service, 'default/route', creator);

      service.collection.push(model);
      expect(service._getFromCollection('myId')).toEqual(model);
      expect(service._getFromCollection('noId')).toBeUndefined();
    });

    it('Should find a model with custom id property value', () => {
      let creator = (data) => data;
      let model = { idField: 'myId' };
      let service = new Service();
      config.registerService('myKey', service, 'default/route', creator, 'idField');

      service.collection.push(model);
      expect(service._getFromCollection('myId')).toEqual(model);
      expect(service._getFromCollection('noId')).toBeUndefined();
    });
  });

  describe('._removeFromCollection', () => {
    let creator;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      service = new Service();
    });

    it('Should remove a model with default _id property value', () => {
      let model = { _id: 'myId' };
      let anotherModel = { idField: 'myOtherId' };
      config.registerService('myKey', service, 'default/route', creator);

      service.collection.push(model);
      service.collection.push(anotherModel);

      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);

      // We can try to remove it  again, and it still works.
      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);
    });

    it('Should remove a model with default _id property value', () => {
      let model = { idField: 'myId' };
      let anotherModel = { idField: 'myOtherId' };
      config.registerService('myKey', service, 'default/route', creator, 'idField');

      service.collection.push(model);
      service.collection.push(anotherModel);

      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);

      // We can try to remove it  again, and it still works.
      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);
    });
  });

  describe('._getById', () => {
    let creator;
    let model;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      model = { _id: 'myId', wheels: 4 };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return an already known model', done => {
      service.collection.push(model);

      service._getById('myId')
        .then(foundModel => {
          expect(foundModel).toBe(model);
          done();
        });
    });

    it('Should return a model when it is not already a known model', done => {
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service._getById('myId')
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the model by calling the fetch method when forcing it', done => {
      service.collection.push(model);
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service._getById('myId', true)
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the model by calling the fetch method is the model is not complete', done => {
      service.isComplete = m => _.has(m, ['_id', 'wheels', 'doors']);

      service.collection.push(model);
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();
      service._getById('myId', true)
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });
  });

  describe('.create', () => {
    let creator;
    let model;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      model = { _id: 'myId', wheels: 4 };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return the created model', done => {
      model = { _id: 'myId', wheels: 4 };
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service.create(model)
        .then(createdModel => {
          expect(createdModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the created model on a dedicated route', done => {
      model = { _id: 'myId', wheels: 4 };
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service.create(model, 'creation')
        .then(createdModel => {
          expect(createdModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the model and leave the submodel id', done => {
      let service2 = new Service();
      let driver = { _id: 'fakeId', name: 'Fake Name' };
      model._ref_driver = 'fakeId';
      config.registerService('Drivers', service2, 'api/drivers', creator);
      service2.collection.push(driver);
      service.refKeys = () => [{ backendKey: '_ref_driver', collection: 'Drivers', frontendKey: 'driver' }];

      fakeFetch.respondWith('{ "_id": "myId", "wheels": 4, "_ref_driver": "fakeId"}');

      service.create(model)
        .then(createdModel => {
          expect(createdModel._id).toEqual('myId');
          expect(createdModel.wheels).toEqual(4);
          expect(createdModel._ref_driver).toEqual(driver._id);
          done();
        });
    });

    it('Should return the model and replace the submodel id by its model when the backend populate its data', done => {
      let service2 = new Service();
      let driver = { _id: 'fakeId', name: 'Fake Name' };
      model._ref_driver = 'fakeId';
      config.registerService('Drivers', service2, 'api/drivers', creator);
      service2.collection.push(driver);
      service.refKeys = () => [{ backendKey: '_ref_driver', collection: 'Drivers', frontendKey: 'driver' }];

      fakeFetch.respondWith('{ "_id": "myId", "wheels": 4, "_ref_driver": { "_id": "fakeId", "name": "Fake Name" } }');

      service.create(model)
        .then(createdModel => {
          expect(createdModel._id).toEqual('myId');
          expect(createdModel.wheels).toEqual(4);
          expect(createdModel.driver).toBe(driver);
          done();
        });
    });
  });
});
<commit_msg>chore(tests): Addition of tests for the destroy method.<commit_after>import { _ } from 'lodash';
import fakeFetch from 'fake-fetch';
import { Container } from 'aurelia-dependency-injection';
import { StageComponent } from 'aurelia-testing';
import { bootstrap } from 'aurelia-bootstrapper';

import { Config } from '../../src/config';
import { Service } from '../../src/service';

describe('Service', () => {
  let component;
  let container;
  let config;

  beforeAll(done => {
    fakeFetch.install();
    component = StageComponent.withResources();
    component.create(bootstrap)
      .then(() => {
        container = Container.instance;
        config = container.get(Config);
        done();
      });
  });

  afterAll(() => {
    fakeFetch.restore();
    component.dispose();
  });

  describe('.configure()', () => {
    it('Should properly initialize the service', () => {
      let service = new Service();
      service.configure('myKey');

      expect(service.container).toBe(container);
      expect(service.modelid).toBe('_id');
      expect(service.modelClass).toBeUndefined();
      expect(service.defaultRoute).toBe('/api/myKey/');
      expect(service.collection instanceof Array).toBe(true);
      expect(service.refKeys()).toEqual([]);
      expect(service.isComplete()).toBeTruthy();
    });
  });

  describe('.fromJSON', () => {
    let service;

    beforeEach(() => {
      let creator = (data) => data;
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return null when data is undefined or null', () => {
      service.fromJSON().then(res => {
        expect(res).toEqual(null);
      });
    });

    it('Should return a new model when data has an id not found in the collection', () => {
      service.fromJSON({ _id: 'myId', foo: 21 }).then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 21 });
      });
    });

    it('Should return a existing model, _syncFrom depending on isComplete, if data id is found in the collection', () => {
      let model = {
        _id: 'myId',
        foo: 42
      };
      service.collection.push(model);

      service.fromJSON({ _id: 'myId' }).then(res => {
        expect(res).toEqual(model);
      });
    });
  });

  describe('.toJSON()', () => {
    let model;
    let service;

    beforeEach(() => {
      let creator = (data) => data;
      model = { _id: 'fakeId', other: 'field' };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return the model when the model doesn\'t have a toJSON method', () => {
      let result = service.toJSON(model);
      expect(result).toEqual(model);
    });

    it('Should call the toJSON method of a model', () => {
      model.toJSON = () => 'myFakeJSON';

      let result = service.toJSON(model);
      expect(result).toBe('myFakeJSON');
    });
  });

  describe('.flush()', () => {
    it('Should clear the collection from the created models', done => {
      fakeFetch.respondWith('{ "_id": "myId" }');

      let service = new Service();
      config.registerService('myKey', service);

      expect(service.collection).toEqual([]);

      service.get('myId')
        .then(res => {
          expect(res._id).toBe('myId');
          service.flush();
          expect(service.collection).toEqual([]);
          done();
        });
    });
  });

  describe('.isComplete', () => {
    it('Should return a boolean', () => {
      let service = new Service();
      expect(service.isComplete({})).toBeTruthy();
    });
  });

  describe('.sync', () => {
    let model;
    let service;
    beforeEach(() => {
      fakeFetch.respondWith('{ "_id": "myId", "foo": "bar" }');
      let creator = (data) => data;
      model = { _id: 'myId' };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return the synced model when given an id', done => {
      service.collection.push(model);
      service.sync('myId').then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 'bar' });
        done();
      });
    });

    it('Should return the synced model when given json data of the model', done => {
      service.collection.push(model);
      service.sync({ _id: 'myId' }).then(res => {
        expect(res).toEqual({ _id: 'myId', foo: 'bar' });
        done();
      });
    });
  });

  describe('.refKeys', () => {
    it('Should return an empty Array', () => {
      let service = new Service();
      expect(service.refKeys()).toEqual([]);
    });
  });

  describe('._setHttpClient', () => {
    it('Should return the same httpClient object', () => {
      let service = new Service();
      service._setHttpClient({ dummy: true });
      expect(service._httpClient).toEqual({ dummy: true });
    });
  });

  describe('._syncFrom', () => {
    it('Should merge two literal objects', () => {
      let foo = { f1: 1 };
      let bar = { f2: 2 };
      let service = new Service();

      service._syncFrom(foo, bar);
      expect(foo).toEqual({ f1: 1, f2: 2 });
    });
  });

  describe('._getFromCollection', () => {
    it('Should find a model with default _id property value', () => {
      let creator = (data) => data;
      let model = { _id: 'myId' };
      let service = new Service();
      config.registerService('myKey', service, 'default/route', creator);

      service.collection.push(model);
      expect(service._getFromCollection('myId')).toEqual(model);
      expect(service._getFromCollection('noId')).toBeUndefined();
    });

    it('Should find a model with custom id property value', () => {
      let creator = (data) => data;
      let model = { idField: 'myId' };
      let service = new Service();
      config.registerService('myKey', service, 'default/route', creator, 'idField');

      service.collection.push(model);
      expect(service._getFromCollection('myId')).toEqual(model);
      expect(service._getFromCollection('noId')).toBeUndefined();
    });
  });

  describe('._removeFromCollection', () => {
    let creator;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      service = new Service();
    });

    it('Should remove a model with default _id property value', () => {
      let model = { _id: 'myId' };
      let anotherModel = { idField: 'myOtherId' };
      config.registerService('myKey', service, 'default/route', creator);

      service.collection.push(model);
      service.collection.push(anotherModel);

      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);

      // We can try to remove it  again, and it still works.
      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);
    });

    it('Should remove a model with default _id property value', () => {
      let model = { idField: 'myId' };
      let anotherModel = { idField: 'myOtherId' };
      config.registerService('myKey', service, 'default/route', creator, 'idField');

      service.collection.push(model);
      service.collection.push(anotherModel);

      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);

      // We can try to remove it  again, and it still works.
      service._removeFromCollection('myId');
      expect(service.collection).toEqual([anotherModel]);
    });
  });

  describe('._getById', () => {
    let creator;
    let model;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      model = { _id: 'myId', wheels: 4 };
      service = new Service();
      config.registerService('myKey', service, 'default/route', creator);
    });

    it('Should return an already known model', done => {
      service.collection.push(model);

      service._getById('myId')
        .then(foundModel => {
          expect(foundModel).toBe(model);
          done();
        });
    });

    it('Should return a model when it is not already a known model', done => {
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service._getById('myId')
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the model by calling the fetch method when forcing it', done => {
      service.collection.push(model);
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service._getById('myId', true)
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });

    it('Should return the model by calling the fetch method is the model is not complete', done => {
      service.isComplete = m => _.has(m, ['_id', 'wheels', 'doors']);

      service.collection.push(model);
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();
      service._getById('myId', true)
        .then(foundModel => {
          expect(foundModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalled();
          done();
        });
    });
  });

  describe('.create', () => {
    let creator;
    let model;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      model = { _id: 'myId', wheels: 4 };
      service = new Service();
      config.registerService('myKey', service, 'default/route/', creator);
    });

    it('Should return the created model', done => {
      model = { _id: 'myId', wheels: 4 };
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service.create(model)
        .then(createdModel => {
          expect(createdModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalledWith('default/route', jasmine.objectContaining({ method: 'post' }));
          done();
        });
    });

    it('Should return the created model on a dedicated route', done => {
      model = { _id: 'myId', wheels: 4 };
      fakeFetch.respondWith(JSON.stringify(model));

      spyOn(service._httpClient, 'fetch').and.callThrough();

      service.create(model, 'creation')
        .then(createdModel => {
          expect(createdModel).toEqual(model);
          expect(service._httpClient.fetch).toHaveBeenCalledWith(('default/route/creation'), jasmine.objectContaining({ method: 'post' }));
          done();
        });
    });

    it('Should return the model and leave the submodel id', done => {
      let service2 = new Service();
      let driver = { _id: 'fakeId', name: 'Fake Name' };
      model._ref_driver = 'fakeId';
      config.registerService('Drivers', service2, 'api/drivers/', creator);
      service2.collection.push(driver);
      service.refKeys = () => [{ backendKey: '_ref_driver', collection: 'Drivers', frontendKey: 'driver' }];

      fakeFetch.respondWith('{ "_id": "myId", "wheels": 4, "_ref_driver": "fakeId"}');

      service.create(model)
        .then(createdModel => {
          expect(createdModel._id).toEqual('myId');
          expect(createdModel.wheels).toEqual(4);
          expect(createdModel._ref_driver).toEqual(driver._id);
          done();
        });
    });

    it('Should return the model and replace the submodel id by its model when the backend populate its data', done => {
      let service2 = new Service();
      let driver = { _id: 'fakeId', name: 'Fake Name' };
      model._ref_driver = 'fakeId';
      config.registerService('Drivers', service2, 'api/drivers/', creator);
      service2.collection.push(driver);
      service.refKeys = () => [{ backendKey: '_ref_driver', collection: 'Drivers', frontendKey: 'driver' }];

      fakeFetch.respondWith('{ "_id": "myId", "wheels": 4, "_ref_driver": { "_id": "fakeId", "name": "Fake Name" } }');

      service.create(model)
        .then(createdModel => {
          expect(createdModel._id).toEqual('myId');
          expect(createdModel.wheels).toEqual(4);
          expect(createdModel.driver).toBe(driver);
          done();
        });
    });
  });

  describe('.destroy()', () => {
    let creator;
    let model;
    let service;

    beforeEach(() => {
      creator = (data) => data;
      model = { _id: 'myId', wheels: 4 };
      service = new Service();
      config.registerService('myKey', service, 'default/route/', creator);
      service.collection.push(model);
      fakeFetch.respondWith('{ "response": "ok" }');
      spyOn(service._httpClient, 'fetch').and.callThrough();
    });

    it('Should remove the model from the collection and call the fetch method', done => {
      service.destroy(model._id)
        .then(answer => {
          expect(service.collection).toEqual([]);
          expect(service._httpClient.fetch).toHaveBeenCalledWith('default/route/' + model._id, { method: 'delete' });
          done();
        });
    });

    it('Should remove the model and call the fetch method on a dedicated route', done => {
      service.destroy(model._id, 'destroy/' + model._id)
        .then(answer => {
          expect(service.collection).toEqual([]);
          expect(service._httpClient.fetch).toHaveBeenCalledWith('default/route/destroy/' + model._id, { method: 'delete' });
          done();
        });
    });
  });
});
<|endoftext|>


<commit_before>(function () {
	'use strict';

	var tokenizer = window.espace.Tokenizer();

	var tr = function (str) {
		var tokens = tokenizer(str);
		var tree = espace.Parser.parse(tokens);
		var ast = cps.buildAst(tree);
		var source = window.cps.tr(ast);
		return new Function(source)();
	};

	describe('tr', function () {
		beforeEach(function () {
			jasmine.addMatchers(cpsTest.customMatchers);
		});

		it('123', function () {
			expect(tr('123')).toEqual(123);
		});

		it('($+ 1 10)', function () {
			expect(tr('($+ 1 10)')).toEqual(11);
		});

		it('($+ 1 ($+ 10 100))', function () {
			expect(tr('($+ 1 ($+ 10 100))')).toEqual(111);
		});

		it('(if 1 10 20)', function () {
			expect(tr('(if 1 10 20)')).toEqual(10);
		});

		it('(if 0 10 20)', function () {
			expect(tr('(if 0 10 20)')).toEqual(20);
		});

		it('(if 1 ($+ 1 10) 20)', function () {
			expect(tr('(if 1 ($+ 1 10) 20)')).toEqual(11);
		});

		it('($+ 1 (if 1 10 20))', function () {
			expect(tr('($+ 1 (if 1 10 20))')).toEqual(11);
		});

		it('(if ($+ 0 1) 10 20)', function () {
			expect(tr('(if ($+ 0 1) 10 20)')).toEqual(10);
		});

		it('(if ($+ 0 0) 10 20)', function () {
			expect(tr('(if ($+ 0 0) 10 20)')).toEqual(20);
		});

		it('(let ((a 123)) a)', function () {
			expect(tr('(let ((a 123)) a)')).toEqual(123);
		});

		it('can bind to "unsafe" identifiers', function () {
			expect(tr('(let ((* 123)) *)')).toEqual(123);
		});

		it('(let ((a 123) (a 456)) a)', function () {
			expect(tr('(let ((a 123) (a 456)) a)')).toEqual(456);
		});

		it('(let ((a 123) (b a)) b)', function () {
			expect(tr('(let ((a 123) (b a)) b)')).toEqual(123);
		});

		it('(let ((a 123)) (set! a 456 a))', function () {
			expect(tr('(let ((a 123)) (set! a 456 a))')).toEqual(456);
		});

		it('($+ 123 (let ((a 456)) a))', function () {
			expect(tr('($+ 123 (let ((a 456)) a))')).toEqual(123 + 456);
		});

		it('((lambda (a) a) 1)', function () {
			expect(tr('((lambda (a) a) 1)')).toEqual(1);
		});

		it('((lambda (a b) ($+ a b)) 1 10)', function () {
			expect(tr('((lambda (a b) ($+ a b)) 1 10)')).toEqual(11);
		});

		it('((lambda (a b c) ($+ ($+ a b) c)) 1 10 100)', function () {
			expect(tr('((lambda (a b c) ($+ ($+ a b) c)) 1 10 100)')).toEqual(111);
		});

		it('((fun f (a) a) 1)', function () {
			expect(tr('((fun f (a) a) 1)')).toEqual(1);
		});

		it('((fun f (a) (if a (f 0) 123)) 1)', function () {
			expect(tr('((fun f (a) (if a (f 0) 123)) 1)')).toEqual(123);
		});

		it('(call/cc (lambda (a) (a 1)))', function () {
			expect(tr('(call/cc (lambda (a) (a 1)))')).toEqual(1);
		});

		it('($+ 1 (call/cc (lambda (a) (call a 10))))', function () {
			expect(tr('($+ 1 (call/cc (lambda (a) (a 10))))')).toEqual(11);
		});

		it('evaluates a complex program', function () {
			expect(tr('((fun sum (n) ' +
			'	(if n ' +
			'		($+ n (sum ($- n 1)))' +
			'		0))' +
			'10)')).toEqual(55);
		});

		it('evaluates a complex call/cc program', function () {
			expect(tr('(let ((return_ 0)) ' +
			'	($+ 1 (call/cc ' +
			'		(lambda (cont_) ' +
			'			(set! return_ cont_ ' +
			'				(return_ 123))))))'
			)).toEqual(124);
		});
	});
})();<commit_msg>Grouped transpiler specs<commit_after>(function () {
	'use strict';

	var tokenizer = window.espace.Tokenizer();

	var tr = function (str) {
		var tokens = tokenizer(str);
		var tree = espace.Parser.parse(tokens);
		var ast = cps.buildAst(tree);
		var source = window.cps.tr(ast);
		return new Function(source)();
	};

	describe('tr', function () {
		beforeEach(function () {
			jasmine.addMatchers(cpsTest.customMatchers);
		});

		describe('data', function () {
			it('123', function () {
				expect(tr('123')).toEqual(123);
			});
		});

		describe('$+', function () {
			it('($+ 1 10)', function () {
				expect(tr('($+ 1 10)')).toEqual(11);
			});

			it('($+ 1 ($+ 10 100))', function () {
				expect(tr('($+ 1 ($+ 10 100))')).toEqual(111);
			});
		});

		describe('$-', function () {
			it('($- 1 10)', function () {
				expect(tr('($- 1 10)')).toEqual(-9);
			});

			it('($+ 1 ($- 10 100))', function () {
				expect(tr('($- 1 ($- 10 100))')).toEqual(91);
			});
		});

		describe('if', function () {
			it('(if 1 10 20)', function () {
				expect(tr('(if 1 10 20)')).toEqual(10);
			});

			it('(if 0 10 20)', function () {
				expect(tr('(if 0 10 20)')).toEqual(20);
			});

			it('(if 1 ($+ 1 10) 20)', function () {
				expect(tr('(if 1 ($+ 1 10) 20)')).toEqual(11);
			});

			it('($+ 1 (if 1 10 20))', function () {
				expect(tr('($+ 1 (if 1 10 20))')).toEqual(11);
			});

			it('(if ($+ 0 1) 10 20)', function () {
				expect(tr('(if ($+ 0 1) 10 20)')).toEqual(10);
			});

			it('(if ($+ 0 0) 10 20)', function () {
				expect(tr('(if ($+ 0 0) 10 20)')).toEqual(20);
			});
		});

		describe('let', function () {
			it('(let ((a 123)) a)', function () {
				expect(tr('(let ((a 123)) a)')).toEqual(123);
			});

			it('can bind to "unsafe" identifiers', function () {
				expect(tr('(let ((* 123)) *)')).toEqual(123);
			});

			it('(let ((a 123) (a 456)) a)', function () {
				expect(tr('(let ((a 123) (a 456)) a)')).toEqual(456);
			});

			it('(let ((a 123) (b a)) b)', function () {
				expect(tr('(let ((a 123) (b a)) b)')).toEqual(123);
			});

			it('(let ((a 123)) (set! a 456 a))', function () {
				expect(tr('(let ((a 123)) (set! a 456 a))')).toEqual(456);
			});

			it('($+ 123 (let ((a 456)) a))', function () {
				expect(tr('($+ 123 (let ((a 456)) a))')).toEqual(123 + 456);
			});
		});

		describe('lambda', function () {
			it('((lambda (a) a) 1)', function () {
				expect(tr('((lambda (a) a) 1)')).toEqual(1);
			});

			it('((lambda (a b) ($+ a b)) 1 10)', function () {
				expect(tr('((lambda (a b) ($+ a b)) 1 10)')).toEqual(11);
			});

			it('((lambda (a b c) ($+ ($+ a b) c)) 1 10 100)', function () {
				expect(tr('((lambda (a b c) ($+ ($+ a b) c)) 1 10 100)')).toEqual(111);
			});
		});

		describe('let', function () {
			it('((fun f (a) a) 1)', function () {
				expect(tr('((fun f (a) a) 1)')).toEqual(1);
			});

			it('((fun f (a) (if a (f 0) 123)) 1)', function () {
				expect(tr('((fun f (a) (if a (f 0) 123)) 1)')).toEqual(123);
			});
		});

		describe('call/cc', function () {
			it('(call/cc (lambda (a) (a 1)))', function () {
				expect(tr('(call/cc (lambda (a) (a 1)))')).toEqual(1);
			});

			it('($+ 1 (call/cc (lambda (a) (call a 10))))', function () {
				expect(tr('($+ 1 (call/cc (lambda (a) (a 10))))')).toEqual(11);
			});
		});

		describe('complex programs', function () {
			it('evaluates a complex program', function () {
				expect(tr('((fun sum (n) ' +
				'	(if n ' +
				'		($+ n (sum ($- n 1)))' +
				'		0))' +
				'10)')).toEqual(55);
			});

			it('evaluates a complex call/cc program', function () {
				expect(tr('(let ((return_ 0)) ' +
					'	($+ 1 (call/cc ' +
					'		(lambda (cont_) ' +
					'			(set! return_ cont_ ' +
					'				(return_ 123))))))'
				)).toEqual(124);
			});
		});
	});
})();<|endoftext|>


<commit_before>'use strict';

describe('Transfer', function() {
  beforeEach(module('transferService'));
  beforeEach(angular.mock.inject(function(_$httpBackend_) {
    _$httpBackend_.when('GET', '/ingest/appraisal_list?field=&query=&type=term').respond({
      'formats': [
        {
          'label': 'Powerpoint 97-2002',
          'puid': 'fmt/126',
        },
      ],
      'transfers': [
        {
          'id': 'd5700e44-68f1-4eec-a7e4-c5a5c7da2373',
          'title': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yw==',
          'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yw==',
          'children': [
            {
              'id': '042340ba-e682-4454-aa26-a9230de79c5f',
              'title': 'cnVieS5yYg==',
              'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yy9ydWJ5LnJi',
              'tags': [],
            },
            {
              'id': 'c3908e0d-3ac7-4603-8d0b-9b26a0df9c64',
              'title': 'c2FwcGhpcmUuY2xq',
              'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yy9zYXBwaGlyZS5jbGo=',
              'tags': [],
            },
          ],
        },
      ],
    });
  }));

  it('should return a JSON array of objects when fetching transfer backlog records', inject(function(_$httpBackend_, Transfer) {
    Transfer.all().then(function(objects) {
        expect(objects.transfers.length).toEqual(1);
        expect(objects.transfers[0].id).toEqual('d5700e44-68f1-4eec-a7e4-c5a5c7da2373');
    });
    _$httpBackend_.flush();
  }));

  it('should be able to track a copy of fetched transfers on itself', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    expect(Transfer.data.length).toEqual(1);
    expect(Transfer.data[0].id).toEqual('d5700e44-68f1-4eec-a7e4-c5a5c7da2373');
  }));

  it('should provide a flat map of all stored transfers using IDs as keys', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    expect(Transfer.id_map['d5700e44-68f1-4eec-a7e4-c5a5c7da2373'].title).toEqual('Images-49c47319-1387-48c4-aab7-381923f07f7c');
  }));

  it('should be able to track a copy of the fetched transfers\' formats on itself', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    expect(Transfer.formats.length).toEqual(1);
    expect(Transfer.formats[0].puid).toEqual('fmt/126');
  }));

  it('should be able to add tags to files', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    expect(ruby.tags.length).toEqual(0);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(1);
  }));

  it('should be able to remove tags for a given file', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(1);
    Transfer.remove_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(0);
  }));

  it('should be able to remove all tags for a given file if no tag is specified', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test1', true);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test2', true);
    expect(ruby.tags.length).toEqual(2);
    Transfer.remove_tag('042340ba-e682-4454-aa26-a9230de79c5f', null, true);
    expect(ruby.tags.length).toEqual(0);
  }));

  it('should track a flat list of all applied tags', inject(function(_$httpBackend_, Transfer) {
    Transfer.resolve();
    _$httpBackend_.flush();
    expect(Transfer.tags).toEqual([]);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test1');
    Transfer.add_tag('c3908e0d-3ac7-4603-8d0b-9b26a0df9c64', 'test2');
    expect(Transfer.tags).toEqual(['test1', 'test2']);
  }))
});
<commit_msg>transferSpec: fix tests<commit_after>'use strict';

describe('Transfer', function() {
  beforeEach(module('transferService'));
  beforeEach(angular.mock.inject(function(Transfer) {
    Transfer.resolve({
      'formats': [
        {
          'label': 'Powerpoint 97-2002',
          'puid': 'fmt/126',
        },
      ],
      'transfers': [
        {
          'id': 'd5700e44-68f1-4eec-a7e4-c5a5c7da2373',
          'title': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yw==',
          'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yw==',
          'children': [
            {
              'id': '042340ba-e682-4454-aa26-a9230de79c5f',
              'title': 'cnVieS5yYg==',
              'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yy9ydWJ5LnJi',
              'tags': [],
            },
            {
              'id': 'c3908e0d-3ac7-4603-8d0b-9b26a0df9c64',
              'title': 'c2FwcGhpcmUuY2xq',
              'relative_path': 'SW1hZ2VzLTQ5YzQ3MzE5LTEzODctNDhjNC1hYWI3LTM4MTkyM2YwN2Y3Yy9zYXBwaGlyZS5jbGo=',
              'tags': [],
            },
          ],
        },
      ],
    });
  }));

  it('should be able to track a copy of fetched transfers on itself', inject(function(Transfer) {
    expect(Transfer.data.length).toEqual(1);
    expect(Transfer.data[0].id).toEqual('d5700e44-68f1-4eec-a7e4-c5a5c7da2373');
  }));

  it('should provide a flat map of all stored transfers using IDs as keys', inject(function(Transfer) {
    expect(Transfer.id_map['d5700e44-68f1-4eec-a7e4-c5a5c7da2373'].title).toEqual('Images-49c47319-1387-48c4-aab7-381923f07f7c');
  }));

  it('should be able to track a copy of the fetched transfers\' formats on itself', inject(function(Transfer) {
    expect(Transfer.formats.length).toEqual(1);
    expect(Transfer.formats[0].puid).toEqual('fmt/126');
  }));

  it('should be able to add tags to files', inject(function(Transfer) {
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    expect(ruby.tags.length).toEqual(0);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(1);
  }));

  it('should be able to remove tags for a given file', inject(function(Transfer) {
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(1);
    Transfer.remove_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test', true);
    expect(ruby.tags.length).toEqual(0);
  }));

  it('should be able to remove all tags for a given file if no tag is specified', inject(function(Transfer) {
    var ruby = Transfer.id_map['042340ba-e682-4454-aa26-a9230de79c5f'];
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test1', true);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test2', true);
    expect(ruby.tags.length).toEqual(2);
    Transfer.remove_tag('042340ba-e682-4454-aa26-a9230de79c5f', null, true);
    expect(ruby.tags.length).toEqual(0);
  }));

  it('should track a flat list of all applied tags', inject(function(Transfer) {
    expect(Transfer.tags).toEqual([]);
    Transfer.add_tag('042340ba-e682-4454-aa26-a9230de79c5f', 'test1');
    Transfer.add_tag('c3908e0d-3ac7-4603-8d0b-9b26a0df9c64', 'test2');
    expect(Transfer.tags).toEqual(['test1', 'test2']);
  }));
});
<|endoftext|>