UNPKG

cipherguard-mad

Version:

Cipherguard javascript application library for the open source password manager for teams

395 lines (345 loc) 14.2 kB
/** * Cipherguard ~ Open source password manager for teams * Copyright (c) KhulnaSoft Ltd (https://www.khulnasoft.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) KhulnaSoft Ltd (https://www.khulnasoft.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.khulnasoft.com KhulnaSoft(tm) */ import $ from 'jquery'; import "cipherguard-mad/test/bootstrap"; import CanControl from "can-control"; import Component from "cipherguard-mad/component/component"; import domEvents from 'can-dom-events'; import MadControl from 'cipherguard-mad/control/control'; import MadMap from 'cipherguard-mad/util/map/map'; import DefineMap from 'cipherguard-mad/model/map/map'; import TreeComponent from "cipherguard-mad/component/tree"; import User from 'cipherguard-mad/test/model/map/user'; import xss from 'cipherguard-mad/test/fixture/xss'; describe("Tree", () => { let $tree = null; beforeEach(() => { $tree = $('<ul id="tree"></ul>').appendTo($('#test-html')); }); afterEach(() => { $('#test-html').empty(); }); describe("constructor", () => { it("inherits dryads", () => { const tree = new TreeComponent('#tree', {}); expect(tree).to.be.instanceOf(CanControl); expect(tree).to.be.instanceOf(MadControl); expect(tree).to.be.instanceOf(Component); expect(tree).to.be.instanceOf(TreeComponent); tree.start(); tree.destroy(); }); }); describe("insertItem()", () => { it("inserts item", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert a first item. const itemInside = new DefineMap({ id: 'item_inside', label: 'item inside label', cssClasses: ['testclass'] }); tree.insertItem(itemInside); expect($('#test-html').text()).to.contain(itemInside.label); expect(tree.options.items.length).to.be.equal(1); // Insert an item before the first one. const itemBefore = new DefineMap({ id: 'item_before', label: 'item before label', cssClasses: ['testclass2'] }); tree.insertItem(itemBefore, itemInside, 'before'); expect(tree.options.items.length).to.be.equal(2); expect($tree.text()).to.contain(itemBefore.label); expect(tree.view.getItemElement(itemInside).prev().attr('id')).to.be.equal('item_before'); // Insert an item after the before one. const itemAfter = new DefineMap({ id: 'item_after', label: 'item after label', cssClasses: ['testclass3'] }); tree.insertItem(itemAfter, itemBefore, 'after'); expect(tree.options.items.length).to.be.equal(3); expect($tree.text()).to.contain(itemInside.label); expect(tree.view.getItemElement(itemInside).prev().attr('id')).to.be.equal('item_after'); expect(tree.view.getItemElement(itemBefore).next().attr('id')).to.be.equal('item_after'); // Insert a child. const itemChildInside = new DefineMap({ id: 'item_child_inside', label: 'item child inside label' }); tree.insertItem(itemChildInside, itemBefore, 'last'); expect(tree.options.items.length).to.be.equal(4); expect($tree.text()).to.contain(itemChildInside.label); expect(tree.view.getItemElement(itemBefore).find('ul:first').children().attr('id')).to.be.equal('item_child_inside'); // Insert a child in first position. const itemChildFirst = new DefineMap({ id: 'item_child_first', label: 'item child first label' }); tree.insertItem(itemChildFirst, itemBefore, 'first'); expect(tree.options.items.length).to.be.equal(5); expect($tree.text()).to.contain(itemChildFirst.label); expect(tree.view.getItemElement(itemChildInside).prev().attr('id')).to.be.equal('item_child_first'); // Test inserting an element in first position without providing a refItem. const itemFirst = new DefineMap({ id: 'item_first', label: 'item first label' }); tree.insertItem(itemFirst, null, 'first'); expect(tree.options.items.length).to.be.equal(6); expect($tree.text()).to.contain(itemFirst.label); expect(tree.view.getItemElement(itemBefore).prev().attr('id')).to.be.equal('item_first'); tree.destroy(); }); }); describe("load()", () => { it('insert batch of items', () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); const items = new DefineMap.List([{ id: 'item_1', label: 'Item 1' }, { id: 'item_2', label: 'Item 2', children: new DefineMap.List([{ id: 'item_21', label: 'Item 21' }, { id: 'item_22', label: 'Item 22' }]) }, { id: 'item_3', label: 'Item 3' }]); tree.load(items); expect(tree.options.items.length).to.be.equal(5); expect($tree.text()).to.contain('Item 1'); expect($tree.text()).to.contain('Item 2'); expect($tree.text()).to.contain('Item 21'); expect($tree.text()).to.contain('Item 22'); expect($tree.text()).to.contain('Item 3'); }); }); describe("removeItem()", () => { it("removes an item - root level", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert items at root level. const items = []; for (let i = 0; i < 5; i++) { items[i] = new DefineMap({ id: `item_inside_${i}`, label: `item inside label ${i}` }); tree.insertItem(items[i]); expect($('#test-html').text()).to.contain(items[i].label); expect(tree.options.items.length).to.be.equal(i + 1); } // Remove an item. tree.removeItem(items[2]); expect(tree.options.items.length).to.be.equal(4); // Check that the item we removed is not present anymore, but the other are still there. expect($('#test-html').text()).not.to.contain(items[2].label); expect($('#test-html').text()).to.contain(items[0].label); expect($('#test-html').text()).to.contain(items[1].label); expect($('#test-html').text()).to.contain(items[3].label); expect($('#test-html').text()).to.contain(items[4].label); tree.destroy(); }); it("removes an item - nested levels", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert items an child items. const items = []; const subItems = []; for (var i = 0; i < 5; i++) { items[i] = new DefineMap({ id: `item_inside_${i}`, label: `item inside label ${i}` }); tree.insertItem(items[i]); expect($('#test-html').text()).to.contain(items[i].label); // Insert nested element. subItems[i] = []; for (var j = 0; j < 5; j++) { subItems[i][j] = new DefineMap({ id: `sub_item_inside_${i}_${j}`, label: `sub item label ${i} ${j}` }); tree.insertItem(subItems[i][j], items[i]); expect($('#test-html').text()).to.contain(subItems[i][j].label); } } const itemsCount = i + i * j; expect(tree.options.items.length).to.be.equal(itemsCount); // Remove an item. tree.removeItem(subItems[2][2]); expect(tree.options.items.length).to.be.equal(itemsCount - 1); // Check that the item we removed is not present anymore, but the other are still there. expect($('#test-html').text()).not.to.contain(subItems[2][2].label); expect($('#test-html').text()).to.contain(subItems[2][0].label); expect($('#test-html').text()).to.contain(subItems[2][1].label); expect($('#test-html').text()).to.contain(subItems[2][3].label); expect($('#test-html').text()).to.contain(subItems[2][4].label); expect($('#test-html').text()).to.contain(items[0].label); expect($('#test-html').text()).to.contain(subItems[0][0].label); expect($('#test-html').text()).to.contain(subItems[0][1].label); expect($('#test-html').text()).to.contain(subItems[0][2].label); expect($('#test-html').text()).to.contain(subItems[0][3].label); expect($('#test-html').text()).to.contain(subItems[0][4].label); expect($('#test-html').text()).to.contain(items[1].label); expect($('#test-html').text()).to.contain(items[3].label); expect($('#test-html').text()).to.contain(items[4].label); // Remove an item. tree.removeItem(items[4]); expect(tree.options.items.length).to.be.equal(itemsCount - 2); // Check that the item we removed is not present anymore, but the other are still there. expect($('#test-html').text()).not.to.contain(subItems[2][2].label); expect($('#test-html').text()).to.contain(subItems[2][0].label); expect($('#test-html').text()).to.contain(subItems[2][1].label); expect($('#test-html').text()).to.contain(subItems[2][3].label); expect($('#test-html').text()).to.contain(subItems[2][4].label); expect($('#test-html').text()).to.contain(items[0].label); expect($('#test-html').text()).to.contain(subItems[0][0].label); expect($('#test-html').text()).to.contain(subItems[0][1].label); expect($('#test-html').text()).to.contain(subItems[0][2].label); expect($('#test-html').text()).to.contain(subItems[0][3].label); expect($('#test-html').text()).to.contain(subItems[0][4].label); expect($('#test-html').text()).to.contain(items[1].label); expect($('#test-html').text()).to.contain(items[3].label); expect($('#test-html').text()).not.to.contain(items[4].label); expect($('#test-html').text()).not.to.contain(subItems[4][0].label); expect($('#test-html').text()).not.to.contain(subItems[4][1].label); expect($('#test-html').text()).not.to.contain(subItems[4][2].label); expect($('#test-html').text()).not.to.contain(subItems[4][3].label); expect($('#test-html').text()).not.to.contain(subItems[4][4].label); tree.destroy(); }); }); describe("refreshItem() ", () => { it("refreshes an item", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert a first item. const item = new DefineMap({ id: 'item_inside', label: 'item inside label' }); tree.insertItem(item); expect(tree.options.items.length).to.be.equal(1); expect($('#test-html').text()).to.contain(item.label); item.label = 'item inside updated label'; tree.refreshItem(item); expect(tree.options.items.length).to.be.equal(1); expect($('#test-html').text()).to.contain(item.label); tree.destroy(); }); }); describe("selectItem() ", () => { it("selects an item", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert an item. const item = new DefineMap({ id: 'item', label: 'item label' }); tree.insertItem(item); // By default an item shouldn't be selected. const $item = $(`#test-html #${item.id}`); expect($('.row:first', $item).hasClass('selected')).to.be.false; // Select an item manually and test that it has been selected. tree.selectItem(item); expect($('.row:first', $item).hasClass('selected')).to.be.true; // Unselect all items. tree.unselectItem(item); expect($('.row:first', $item).hasClass('selected')).to.be.false; // Select an item by clicking on it. $('a', $item).trigger('click'); /* *expect($('.row:first', $item).hasClass('selected')).to.be.true; * *tree.destroy(); */ }); it("selectItem() should select an item in the tree after refresh", () => { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); // Insert an item. const item = new DefineMap({ id: 'item', label: 'item label' }); tree.insertItem(item); item.label = 'item label updated'; tree.refreshItem(item); // By default an item shouldn't be selected. const $item = $(`#test-html #${item.id}`); expect($('.row:first', $item).hasClass('selected')).to.be.false; // Select an item manually and test that it has been selected. tree.selectItem(item); expect($('.row:first', $item).hasClass('selected')).to.be.true; // Unselect all items. tree.unselectItem(item); expect($('.row:first', $item).hasClass('selected')).to.be.false; // Select an item by clicking on it. domEvents.dispatch($('a', $item)[0], 'click'); expect($('.row:first', $item).hasClass('selected')).to.be.true; tree.destroy(); }); }); /* * Ensure the grid is not vulnerable to xss. * - When inserting an item, take care of: * - The attribute id of the li row which is based by the item id property * - The cell value which is based on the mapped value, here the property label * - Check the item selection */ describe("security ", () => { it("is not vulnerable to XSS attack", () => { for (const rule in xss) { const tree = new TreeComponent('#tree', { itemClass: DefineMap }); tree.start(); const item = new DefineMap({ id: xss[rule], label: xss[rule] }); // No Xss when inserting the item tree.insertItem(item); // No Xss when clicking on the row which as the id attribute domEvents.dispatch($('#tree li')[0], 'click'); tree.destroy(); } }); }); });