Mailspring/app/spec/list-selection-spec.ts
Ben Gotow 149b389508
Replace Babel with TypeScript compiler, switch entire app to TypeScript 🎉 (#1404)
* Switch to using Typescript instead of Babel

* Switch all es6 / jsx file extensions to ts / tsx

* Convert Utils to a TS module from module.exports style module

* Move everything from module.exports to typescript exports

* Define .d.ts files for mailspring-exports and component kit… Yes it seems this is the best option :(

* Load up on those @types

* Synthesize TS types from PropTypes for standard components

* Add types to Model classes and move constructor constants to instance vars

* 9800 => 7700 TS errors

* 7700 => 5600 TS errors

* 5600 => 5330 TS errors

* 5330 => 4866 TS errors

* 4866 => 4426 TS errors

* 4426 => 2411 TS errors

* 2411 > 1598 TS errors

* 1598 > 769 TS errors

* 769 > 129 TS errors

* 129 > 22 TS errors

* Fix runtime errors

* More runtime error fixes

* Remove support for custom .es6 file extension

* Remove a few odd remaining references to Nylas

* Don’t ship Typescript support in the compiled app for now

* Fix issues in compiled app - module resolution in TS is case sensitive?

* README updates

* Fix a few more TS errors

* Make “No Signature” option clickable + selectable

* Remove flicker when saving file and reloading keymaps

* Fix mail rule item height in preferences

* Fix missing spacing in thread sharing popover

* Fix scrollbar ticks being nested incorrectly

* Add Japanese as a manually reviewed language

* Prevent the thread list from “sticking”

* Re-use Sheet when switching root tabs, prevent sidebar from resetting

* Ensure specs run

* Update package configuration to avoid shpping types

* Turn eslint back on - we will opt-in to the TS rules one by one
2019-03-04 11:03:12 -08:00

273 lines
9.7 KiB
TypeScript

import _ from 'underscore';
import { Thread } from 'mailspring-exports';;
import { ListTabular } from 'mailspring-component-kit';;
const ListDataSource = ListTabular.DataSource;
const ListSelection = ListTabular.Selection;
describe('ListSelection', function() {
beforeEach(function() {
this.trigger = jasmine.createSpy('trigger');
this.items = [];
for (let ii = 0; ii <= 99; ii++) {
this.items.push(new Thread({ id: `${ii}` }));
}
this.view = new ListDataSource();
this.view.indexOfId = jasmine.createSpy('indexOfId').andCallFake(id => {
return _.findIndex(this.items, _.matcher({ id }));
});
this.view.get = jasmine.createSpy('get').andCallFake(idx => {
return this.items[idx];
});
this.selection = new ListSelection(this.view, this.trigger);
});
it('should initialize with an empty set', function() {
expect(this.selection.items()).toEqual([]);
expect(this.selection.ids()).toEqual([]);
});
it('should throw an exception if a view is not provided', function() {
expect(() => new ListSelection(null, this.trigger)).toThrow();
});
describe('set', function() {
it('should replace the current selection with the provided models', function() {
this.selection.set([this.items[2], this.items[4], this.items[7]]);
expect(this.selection.ids()).toEqual(['2', '4', '7']);
this.selection.set([this.items[2], this.items[5], this.items[6]]);
expect(this.selection.ids()).toEqual(['2', '5', '6']);
});
it('should throw an exception if the items passed are not models', function() {
expect(() => this.selection.set(['hi'])).toThrow();
});
it('should trigger', function() {
this.selection.set([this.items[2], this.items[4], this.items[7]]);
expect(this.trigger).toHaveBeenCalled();
});
});
describe('clear', function() {
beforeEach(function() {
this.selection.set([this.items[2]]);
});
it('should empty the selection set', function() {
this.selection.clear();
expect(this.selection.ids()).toEqual([]);
});
it('should trigger', function() {
this.selection.clear();
expect(this.trigger).toHaveBeenCalled();
});
});
describe('remove', function() {
beforeEach(function() {
this.selection.set([this.items[2], this.items[4], this.items[7]]);
});
it('should do nothing if called without a valid item', function() {
this.selection.remove(null);
this.selection.remove(undefined);
this.selection.remove(false);
expect(this.selection.ids()).toEqual(['2', '4', '7']);
});
it('should remove the item from the set', function() {
this.selection.remove(this.items[2]);
expect(this.selection.ids()).toEqual(['4', '7']);
});
it('should throw an exception if any item passed is not a model', function() {
expect(() => this.selection.remove('hi')).toThrow();
});
it('should accept an array of models as well as a single item', function() {
this.selection.remove([this.items[2], this.items[4]]);
expect(this.selection.ids()).toEqual(['7']);
});
it('should trigger', function() {
this.selection.remove();
expect(this.trigger).toHaveBeenCalled();
});
});
describe('_applyChangeRecord', function() {
it('should replace items in the selection with the matching provided items, if present', function() {
this.selection.set([this.items[2], this.items[4], this.items[7]]);
expect(this.selection.items()[0]).toBe(this.items[2]);
expect(this.selection.items()[0].subject).toBe(undefined);
const newItem2 = new Thread({ id: '2', subject: 'Hello world!' });
this.selection._applyChangeRecord({
objectClass: 'Thread',
objects: [newItem2],
type: 'persist',
});
expect(this.selection.items()[0].subject).toBe('Hello world!');
});
it('should rremove items in the selection if type is unpersist', function() {
this.selection.set([this.items[2], this.items[4], this.items[7]]);
const newItem2 = new Thread({ id: '2', subject: 'Hello world!' });
this.selection._applyChangeRecord({
objectClass: 'Thread',
objects: [newItem2],
type: 'unpersist',
});
expect(this.selection.ids()).toEqual(['4', '7']);
});
});
describe('toggle', function() {
beforeEach(function() {
this.selection.set([this.items[2]]);
});
it('should do nothing if called without a valid item', function() {
this.selection.toggle(null);
this.selection.toggle(undefined);
this.selection.toggle(false);
expect(this.selection.ids()).toEqual(['2']);
});
it('should throw an exception if the item passed is not a model', function() {
expect(() => this.selection.toggle('hi')).toThrow();
});
it('should select the item if it is not selected', function() {
this.selection.toggle(this.items[3]);
expect(this.selection.ids()).toEqual(['2', '3']);
});
it('should de-select the item if it is selected', function() {
this.selection.toggle(this.items[2]);
expect(this.selection.ids()).toEqual([]);
});
it('should trigger', function() {
this.selection.toggle(this.items[2]);
expect(this.trigger).toHaveBeenCalled();
});
});
describe('expandTo', function() {
it('should select the item, if no other items are selected', function() {
this.selection.clear();
this.selection.expandTo(this.items[2]);
expect(this.selection.ids()).toEqual(['2']);
});
it('should do nothing if called without a valid item', function() {
this.selection.expandTo(null);
this.selection.expandTo(undefined);
this.selection.expandTo(false);
expect(this.selection.ids()).toEqual([]);
});
it('should throw an exception if the item passed is not a model', function() {
expect(() => this.selection.expandTo('hi')).toThrow();
});
it('should select all items from the last selected item to the provided item when the provided item is below the current selection', function() {
this.selection.set([this.items[2], this.items[5]]);
this.selection.expandTo(this.items[8]);
expect(this.selection.ids()).toEqual(['2', '5', '6', '7', '8']);
});
it('should select all items from the last selected item to the provided item when the provided item is above the current selection', function() {
this.selection.set([this.items[7], this.items[5]]);
this.selection.expandTo(this.items[2]);
expect(this.selection.ids()).toEqual(['7', '5', '4', '3', '2']);
});
it('should not do anything if the provided item is not in the view set', function() {
this.selection.set([this.items[2]]);
this.selection.expandTo(new Thread({ id: 'not-in-view!' }));
expect(this.selection.ids()).toEqual(['2']);
});
it('should re-order items so that the order still reflects the order selection actions were taken', function() {
this.selection.set([this.items[10], this.items[4], this.items[1]]);
this.selection.expandTo(this.items[8]);
expect(this.selection.ids()).toEqual(['10', '1', '2', '3', '4', '5', '6', '7', '8']);
});
it('should trigger', function() {
this.selection.set([this.items[5], this.items[4], this.items[1]]);
this.selection.expandTo(this.items[8]);
expect(this.trigger).toHaveBeenCalled();
});
});
describe('walk', function() {
beforeEach(function() {
this.selection.set([this.items[2]]);
});
it('should trigger', function() {
const current = this.items[4];
const next = this.items[5];
this.selection.walk({ current, next });
expect(this.trigger).toHaveBeenCalled();
});
it('should select both items if neither the start row or the end row are selected', function() {
const current = this.items[4];
const next = this.items[5];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['2', '4', '5']);
});
it('should select only one item if either current or next is null or undefined', function() {
let current = null;
let next = this.items[5];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['2', '5']);
next = null;
current = this.items[7];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['2', '5', '7']);
});
describe('when the `next` item is a step backwards in the selection history', () =>
it('should deselect the current item', function() {
this.selection.set([this.items[2], this.items[3], this.items[4], this.items[5]]);
const current = this.items[5];
const next = this.items[4];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['2', '3', '4']);
}));
describe('otherwise', function() {
it('should select the next item', function() {
this.selection.set([this.items[2], this.items[3], this.items[4], this.items[5]]);
const current = this.items[5];
const next = this.items[6];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['2', '3', '4', '5', '6']);
});
describe('if the item was already selected', () =>
it('should re-order the selection array so the selection still represents selection history', function() {
this.selection.set([this.items[5], this.items[8], this.items[7], this.items[6]]);
expect(this.selection.ids()).toEqual(['5', '8', '7', '6']);
const current = this.items[6];
const next = this.items[5];
this.selection.walk({ current, next });
expect(this.selection.ids()).toEqual(['8', '7', '6', '5']);
}));
});
});
});