diff --git a/.eslintrc b/.eslintrc index a435b530e..6e678b961 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,7 @@ { "parser": "@typescript-eslint/parser", "parserOptions": { - "project": "./tsconfig.json" + "project": "./app/tsconfig.json" }, "globals": { "AppEnv": false, diff --git a/app/internal_packages/activity/specs/activity-list-spec.jsx b/app/internal_packages/activity/specs/activity-list-spec.jsx deleted file mode 100644 index cef89a37d..000000000 --- a/app/internal_packages/activity/specs/activity-list-spec.jsx +++ /dev/null @@ -1,232 +0,0 @@ -import React from 'react'; -import ReactTestUtils from 'react-dom/test-utils'; -import { - Thread, - Actions, - Contact, - Message, - DatabaseStore, - FocusedPerspectiveStore, -} from 'mailspring-exports'; -import ActivityList from '../lib/list/activity-list'; -import ActivityEventStore from '../lib/activity-event-store'; -import TestDataSource from '../lib/test-data-source'; -import { OPEN_TRACKING_ID, LINK_TRACKING_ID } from '../lib/plugin-helpers'; - -const messages = [ - new Message({ - id: 'a', - accountId: '0000000000000000000000000', - bcc: [], - cc: [], - snippet: 'Testing.', - subject: 'Open me!', - threadId: '0000000000000000000000000', - to: [ - new Contact({ - name: 'Jackie Luo', - email: 'jackie@nylas.com', - }), - ], - }), - new Message({ - id: 'b', - accountId: '0000000000000000000000000', - bcc: [ - new Contact({ - name: 'Ben Gotow', - email: 'ben@nylas.com', - }), - ], - cc: [], - snippet: 'Hey! I am in town for the week...', - subject: 'Coffee?', - threadId: '0000000000000000000000000', - to: [ - new Contact({ - name: 'Jackie Luo', - email: 'jackie@nylas.com', - }), - ], - }), - new Message({ - id: 'c', - accountId: '0000000000000000000000000', - bcc: [], - cc: [ - new Contact({ - name: 'Evan Morikawa', - email: 'evan@nylas.com', - }), - ], - snippet: "Here's the latest deals!", - subject: 'Newsletter', - threadId: '0000000000000000000000000', - to: [ - new Contact({ - name: 'Juan Tejada', - email: 'juan@nylas.com', - }), - ], - }), -]; - -let pluginValue = { - open_count: 1, - open_data: [ - { - timestamp: 1461361759.351055, - }, - ], -}; -messages[0].directlyAttachMetadata(OPEN_TRACKING_ID, pluginValue); -pluginValue = { - links: [ - { - click_count: 1, - click_data: [ - { - timestamp: 1461349232.495837, - }, - ], - }, - ], - tracked: true, -}; -messages[0].directlyAttachMetadata(LINK_TRACKING_ID, pluginValue); -pluginValue = { - open_count: 1, - open_data: [ - { - timestamp: 1461361763.28372, - }, - ], -}; -messages[1].directlyAttachMetadata(OPEN_TRACKING_ID, pluginValue); -pluginValue = {}; -messages[1].directlyAttachMetadata(LINK_TRACKING_ID, pluginValue); -pluginValue = { - open_count: 0, - open_data: [], -}; -messages[2].directlyAttachMetadata(OPEN_TRACKING_ID, pluginValue); -pluginValue = { - links: [ - { - click_count: 0, - click_data: [], - }, - ], - tracked: true, -}; -messages[2].directlyAttachMetadata(LINK_TRACKING_ID, pluginValue); - -describe('ActivityList', function activityList() { - beforeEach(() => { - this.testSource = new TestDataSource(); - spyOn(ActivityEventStore, '_dataSource').andReturn(this.testSource); - spyOn(FocusedPerspectiveStore, 'sidebarAccountIds').andReturn([ - '0000000000000000000000000', - ]); - spyOn(DatabaseStore, 'run').andCallFake(query => { - if (query._klass === Thread) { - const thread = new Thread({ - id: '0000000000000000000000000', - accountId: TEST_ACCOUNT_ID, - }); - return Promise.resolve(thread); - } - return null; - }); - spyOn(ActivityEventStore, 'focusThread').andCallThrough(); - spyOn(AppEnv, 'displayWindow'); - spyOn(Actions, 'closePopover'); - spyOn(Actions, 'setFocus'); - spyOn(Actions, 'ensureCategoryIsFocused'); - ActivityEventStore.activate(); - this.component = ReactTestUtils.renderIntoDocument(); - }); - - describe('when no actions are found', () => { - it('should show empty state', () => { - const items = ReactTestUtils.scryRenderedDOMComponentsWithClass( - this.component, - 'activity-list-item' - ); - expect(items.length).toBe(0); - }); - }); - - describe('when actions are found', () => { - it('should show activity list items', () => { - this.testSource.manuallyTrigger(messages); - waitsFor(() => { - const items = ReactTestUtils.scryRenderedDOMComponentsWithClass( - this.component, - 'activity-list-item' - ); - return items.length > 0; - }); - runs(() => { - expect( - ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'activity-list-item') - .length - ).toBe(3); - }); - }); - - it('should show the correct items', () => { - this.testSource.manuallyTrigger(messages); - waitsFor(() => { - const items = ReactTestUtils.scryRenderedDOMComponentsWithClass( - this.component, - 'activity-list-item' - ); - return items.length > 0; - }); - runs(() => { - expect( - ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'activity-list-item')[0] - .textContent - ).toBe('Someone opened:Apr 22 2016Coffee?'); - expect( - ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'activity-list-item')[1] - .textContent - ).toBe('Jackie Luo opened:Apr 22 2016Open me!'); - expect( - ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'activity-list-item')[2] - .textContent - ).toBe('Jackie Luo clicked:Apr 22 2016(No Subject)'); - }); - }); - - xit('should focus the thread', () => { - runs(() => { - return this.testSource.manuallyTrigger(messages); - }); - waitsFor(() => { - const items = ReactTestUtils.scryRenderedDOMComponentsWithClass( - this.component, - 'activity-list-item' - ); - return items.length > 0; - }); - runs(() => { - const item = ReactTestUtils.scryRenderedDOMComponentsWithClass( - this.component, - 'activity-list-item' - )[0]; - ReactTestUtils.Simulate.click(item); - }); - waitsFor(() => { - return ActivityEventStore.focusThread.calls.length > 0; - }); - runs(() => { - expect(AppEnv.displayWindow.calls.length).toBe(1); - expect(Actions.closePopover.calls.length).toBe(1); - expect(Actions.setFocus.calls.length).toBe(1); - expect(Actions.ensureCategoryIsFocused.calls.length).toBe(1); - }); - }); - }); -}); diff --git a/app/internal_packages/composer-signature/lib/signature-composer-dropdown.tsx b/app/internal_packages/composer-signature/lib/signature-composer-dropdown.tsx index 0dfb4be36..358bf6194 100644 --- a/app/internal_packages/composer-signature/lib/signature-composer-dropdown.tsx +++ b/app/internal_packages/composer-signature/lib/signature-composer-dropdown.tsx @@ -11,7 +11,7 @@ import { } from 'mailspring-exports'; import { Menu, RetinaImg, ButtonDropdown } from 'mailspring-component-kit'; -import { applySignature, currentSignatureIdSlate } from './signature-utils'; +import { applySignature, currentSignatureIdSlate, currentSignatureId } from './signature-utils'; const MenuItem = Menu.Item; @@ -102,7 +102,12 @@ export default class SignatureComposerDropdown extends React.Component< _renderSignatures() { // note: these are using onMouseDown to avoid clearing focus in the composer (I think) - const currentId = currentSignatureIdSlate(this.props.draft.bodyEditorState); + let currentId: string; + if (AppEnv.inSpecMode()) { + currentId = currentSignatureId(this.props.draft.body); + } else { + currentId = currentSignatureIdSlate(this.props.draft.bodyEditorState); + } return ( - {this._renderSendActionItem(this.state.sendActions[0])} - - ); - } - - _renderButtonDropdown() { - const { sendActions } = this.state; - const menu = ( - actionConfig.configKey} - itemContent={this._renderSendActionItem} - onSelect={this._onSendWithAction} - /> - ); - + render() { return ( actionConfig.configKey} + itemContent={this._renderSendActionItem} + onSelect={this._onSendWithAction} + /> + } /> ); } - - render() { - return this.state.sendActions.length === 1 - ? this._renderSingleButton() - : this._renderButtonDropdown(); - } } diff --git a/app/internal_packages/composer/specs/composer-header-spec.jsx b/app/internal_packages/composer/specs/composer-header-spec.jsx index a727adf83..374bd7671 100644 --- a/app/internal_packages/composer/specs/composer-header-spec.jsx +++ b/app/internal_packages/composer/specs/composer-header-spec.jsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom'; import ReactTestUtils from 'react-dom/test-utils'; import { Contact, Message } from 'mailspring-exports'; -import ComposerHeader from '../lib/composer-header'; +import { ComposerHeader } from '../lib/composer-header'; import Fields from '../lib/fields'; const DRAFT_HEADER_MSG_ID = 'DRAFT_HEADER_MSG_ID'; diff --git a/app/internal_packages/composer/specs/send-action-button-spec.jsx b/app/internal_packages/composer/specs/send-action-button-spec.jsx index 57d69481f..4e5acff0f 100644 --- a/app/internal_packages/composer/specs/send-action-button-spec.jsx +++ b/app/internal_packages/composer/specs/send-action-button-spec.jsx @@ -2,9 +2,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { ButtonDropdown, RetinaImg } from 'mailspring-component-kit'; import { Actions, Message, SendActionsStore } from 'mailspring-exports'; -import SendActionButton from '../lib/send-action-button'; - -const { UndecoratedSendActionButton } = SendActionButton; +import { SendActionButton } from '../lib/send-action-button'; const GoodSendAction = { title: 'Good Send Action', @@ -36,41 +34,22 @@ describe('SendActionButton', function describeBlock() { this.draft = new Message({ id: this.id, draft: true, headerMessageId: 'bla' }); }); - const render = (draft, { isValid = true, sendActions } = {}) => { + const render = (draft, { isValid = true } = {}) => { this.isValidDraft.andReturn(isValid); - return mount( - - ); + return mount(); }; it('renders without error', () => { const sendActionButton = render(this.draft); - expect(sendActionButton.is(UndecoratedSendActionButton)).toBe(true); + expect(sendActionButton.is(SendActionButton)).toBe(true); }); - it('initializes with the default and shows the standard Send option', () => { + it('is a dropdown', () => { + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + SendActionsStore.DefaultSendAction, + GoodSendAction, + ]); const sendActionButton = render(this.draft); - const button = sendActionButton.find('button').first(); - expect(button.text()).toEqual('Send'); - }); - - it('is a single button when there is only the default actions', () => { - const sendActionButton = render(this.draft); - const dropdowns = sendActionButton.find(ButtonDropdown); - const buttons = sendActionButton.find('button'); - expect(buttons.length).toBe(1); - expect(dropdowns.length).toBe(0); - expect(buttons.first().text()).toBe('Send'); - }); - - it("is a dropdown when there's more than one send action", () => { - const sendActionButton = render(this.draft, { - sendActions: [SendActionsStore.DefaultSendAction, GoodSendAction], - }); const dropdowns = sendActionButton.find(ButtonDropdown); const buttons = sendActionButton.find('button'); expect(buttons.length).toBe(0); @@ -79,17 +58,22 @@ describe('SendActionButton', function describeBlock() { }); it('has the correct primary item', () => { - const sendActionButton = render(this.draft, { - sendActions: [SecondSendAction, SendActionsStore.DefaultSendAction, GoodSendAction], - }); + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + SecondSendAction, + SendActionsStore.DefaultSendAction, + GoodSendAction, + ]); + const sendActionButton = render(this.draft); const dropdown = sendActionButton.find(ButtonDropdown).first(); expect(dropdown.prop('primaryTitle')).toBe('Second Send Action'); }); it("still renders with a null iconUrl and doesn't show the image", () => { - const sendActionButton = render(this.draft, { - sendActions: [NoIconUrl, SendActionsStore.DefaultSendAction], - }); + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + NoIconUrl, + SendActionsStore.DefaultSendAction, + ]); + const sendActionButton = render(this.draft); const dropdowns = sendActionButton.find(ButtonDropdown); const buttons = sendActionButton.find('button'); const icons = sendActionButton.find(RetinaImg); @@ -99,6 +83,10 @@ describe('SendActionButton', function describeBlock() { }); it('sends a draft by default if no extra actions present', () => { + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + SendActionsStore.DefaultSendAction, + GoodSendAction, + ]); const sendActionButton = render(this.draft); const button = sendActionButton.find('button').first(); button.simulate('click'); @@ -109,6 +97,10 @@ describe('SendActionButton', function describeBlock() { }); it("doesn't send a draft if the isValidDraft fails", () => { + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + SendActionsStore.DefaultSendAction, + GoodSendAction, + ]); const sendActionButton = render(this.draft, { isValid: false }); const button = sendActionButton.find('button').first(); button.simulate('click'); @@ -117,9 +109,11 @@ describe('SendActionButton', function describeBlock() { }); it('does the preferred action when more than one action present', () => { - const sendActionButton = render(this.draft, { - sendActions: [GoodSendAction, SendActionsStore.DefaultSendAction], - }); + spyOn(SendActionsStore, 'orderedSendActionsForDraft').andReturn([ + GoodSendAction, + SendActionsStore.DefaultSendAction, + ]); + const sendActionButton = render(this.draft, {}); const button = sendActionButton.find('.primary-item').first(); button.simulate('click'); expect(this.isValidDraft).toHaveBeenCalled(); diff --git a/app/internal_packages/message-list/specs/message-list-spec.jsx b/app/internal_packages/message-list/specs/message-list-spec.jsx index 1097ee503..63dc5a8f5 100644 --- a/app/internal_packages/message-list/specs/message-list-spec.jsx +++ b/app/internal_packages/message-list/specs/message-list-spec.jsx @@ -58,7 +58,7 @@ const m1 = new Message({ subject: 'Subject One', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }); const m2 = new Message({ id: '222', @@ -75,7 +75,7 @@ const m2 = new Message({ subject: 'Subject Two', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }); const m3 = new Message({ id: '333', @@ -92,7 +92,7 @@ const m3 = new Message({ subject: 'Subject Three', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }); const m4 = new Message({ id: '444', @@ -109,7 +109,7 @@ const m4 = new Message({ subject: 'Subject Four', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }); const m5 = new Message({ id: '555', @@ -126,7 +126,7 @@ const m5 = new Message({ subject: 'Subject Five', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }); const testMessages = [m1, m2, m3, m4, m5]; const draftMessages = [ @@ -146,7 +146,7 @@ const draftMessages = [ subject: 'Draft One', threadId: 'thread_12345', accountId: TEST_ACCOUNT_ID, - folder: new Folder({ role: 'all', name: 'All Mail' }), + folder: new Folder({ role: 'all', path: 'All Mail' }), }), ]; diff --git a/app/internal_packages/system-tray/specs/system-tray-icon-store-spec.ts b/app/internal_packages/system-tray/specs/system-tray-icon-store-spec.ts index 993e7b243..86b880403 100644 --- a/app/internal_packages/system-tray/specs/system-tray-icon-store-spec.ts +++ b/app/internal_packages/system-tray/specs/system-tray-icon-store-spec.ts @@ -2,7 +2,7 @@ import { ipcRenderer } from 'electron'; import { BadgeStore } from 'mailspring-exports'; import SystemTrayIconStore from '../lib/system-tray-icon-store'; -const { INBOX_ZERO_ICON, INBOX_UNREAD_ICON, INBOX_UNREAD_ALT_ICON } = SystemTrayIconStore; +const { INBOX_ZERO_ICON, INBOX_FULL_ICON, INBOX_FULL_UNREAD_ICON } = SystemTrayIconStore; describe('SystemTrayIconStore', function systemTrayIconStore() { beforeEach(() => { @@ -12,32 +12,39 @@ describe('SystemTrayIconStore', function systemTrayIconStore() { function getCallData() { const { args } = (ipcRenderer.send as any).calls[0]; - return { iconPath: args[1], isTemplateImg: args[3] }; + return { path: args[1], isTemplateImg: args[3] }; } describe('_getIconImageData', () => { it('shows inbox zero icon when isInboxZero and window is focused', () => { - const { iconPath, isTemplateImg } = this.iconStore._getIconImageData(true, false); - expect(iconPath).toBe(INBOX_ZERO_ICON); - expect(isTemplateImg).toBe(true); + spyOn(BadgeStore, 'unread').andReturn(0); + spyOn(BadgeStore, 'total').andReturn(0); + this.iconStore._updateIcon(); + expect(getCallData()).toEqual({ path: INBOX_ZERO_ICON, isTemplateImg: true }); }); it('shows inbox zero icon when isInboxZero and window is blurred', () => { - const { iconPath, isTemplateImg } = this.iconStore._getIconImageData(true, true); - expect(iconPath).toBe(INBOX_ZERO_ICON); - expect(isTemplateImg).toBe(true); + this.iconStore._windowBlurred = true; + spyOn(BadgeStore, 'unread').andReturn(0); + spyOn(BadgeStore, 'total').andReturn(0); + this.iconStore._updateIcon(); + expect(getCallData()).toEqual({ path: INBOX_ZERO_ICON, isTemplateImg: true }); }); it('shows inbox full icon when not isInboxZero and window is focused', () => { - const { iconPath, isTemplateImg } = this.iconStore._getIconImageData(false, false); - expect(iconPath).toBe(INBOX_UNREAD_ICON); - expect(isTemplateImg).toBe(true); + this.iconStore._windowBlurred = false; + spyOn(BadgeStore, 'unread').andReturn(102); + spyOn(BadgeStore, 'total').andReturn(123123); + this.iconStore._updateIcon(); + expect(getCallData()).toEqual({ path: INBOX_FULL_ICON, isTemplateImg: true }); }); it('shows inbox full /alt/ icon when not isInboxZero and window is blurred', () => { - const { iconPath, isTemplateImg } = this.iconStore._getIconImageData(false, true); - expect(iconPath).toBe(INBOX_UNREAD_ALT_ICON); - expect(isTemplateImg).toBe(false); + this.iconStore._windowBlurred = true; + spyOn(BadgeStore, 'unread').andReturn(102); + spyOn(BadgeStore, 'total').andReturn(123123); + this.iconStore._updateIcon(); + expect(getCallData()).toEqual({ path: INBOX_FULL_UNREAD_ICON, isTemplateImg: false }); }); }); @@ -45,8 +52,8 @@ describe('SystemTrayIconStore', function systemTrayIconStore() { it('always shows inbox full icon when the window gets focused', () => { spyOn(BadgeStore, 'total').andReturn(1); this.iconStore._onWindowFocus(); - const { iconPath } = getCallData(); - expect(iconPath).toBe(INBOX_UNREAD_ICON); + const { path } = getCallData(); + expect(path).toBe(INBOX_FULL_ICON); }); it('shows inbox full /alt/ icon ONLY when window is currently blurred and total count changes', () => { @@ -58,8 +65,8 @@ describe('SystemTrayIconStore', function systemTrayIconStore() { spyOn(BadgeStore, 'total').andReturn(1); this.iconStore._updateIcon(); - const { iconPath } = getCallData(); - expect(iconPath).toBe(INBOX_UNREAD_ALT_ICON); + const { path } = getCallData(); + expect(path).toBe(INBOX_FULL_UNREAD_ICON); }); it('does not show inbox full /alt/ icon when window is currently focused and total count changes', () => { @@ -69,8 +76,8 @@ describe('SystemTrayIconStore', function systemTrayIconStore() { spyOn(BadgeStore, 'total').andReturn(1); this.iconStore._updateIcon(); - const { iconPath } = getCallData(); - expect(iconPath).toBe(INBOX_UNREAD_ICON); + const { path } = getCallData(); + expect(path).toBe(INBOX_FULL_ICON); }); }); }); diff --git a/app/spec/components/date-input-spec.tsx b/app/spec/components/date-input-spec.tsx index e88373c09..ff608aa7f 100644 --- a/app/spec/components/date-input-spec.tsx +++ b/app/spec/components/date-input-spec.tsx @@ -14,7 +14,7 @@ describe('DateInput', function dateInput() { const component = mount(); const inputNode = component.find('input'); const stopPropagation = jasmine.createSpy('stopPropagation'); - inputNode.getDOMNode().value = 'tomorrow'; + (inputNode.getDOMNode() as any).value = 'tomorrow'; inputNode.simulate('keyDown', { key, stopPropagation }); expect(stopPropagation).toHaveBeenCalled(); @@ -29,16 +29,20 @@ describe('DateInput', function dateInput() { }); it('should render a date interpretation if a date has been inputted', () => { - const component = mount(); + const component = mount( + + ); spyOn(component, 'setState'); const dateInterpretation = component.find('.date-interpretation'); expect(dateInterpretation.text()).toEqual('formatted'); }); it('should not render a date interpretation if no input date available', () => { - const component = mount(); + const component = mount( + + ); spyOn(component, 'setState'); - expect(component.find('.date-interpretation').isEmpty()).toBe(true); + expect(component.find('.date-interpretation').exists()).toBe(false); }); }); }); diff --git a/app/spec/components/editable-table-spec.tsx b/app/spec/components/editable-table-spec.tsx index 07d866054..8ce4e9b40 100644 --- a/app/spec/components/editable-table-spec.tsx +++ b/app/spec/components/editable-table-spec.tsx @@ -80,7 +80,8 @@ describe('EditableTable Components', function describeBlock() { }); it('renders the InputRenderer as the child of the SelectableTableCell with the correct props', () => { - const InputRenderer = props => ; + const TestInput = props => ; + const InputRenderer = props => ; const inputProps = { p1: 'p1' }; const input = renderCell({ rowIdx: 2, diff --git a/app/spec/components/table/table-spec.tsx b/app/spec/components/table/table-spec.tsx index b9f6868d8..63d5c71b8 100644 --- a/app/spec/components/table/table-spec.tsx +++ b/app/spec/components/table/table-spec.tsx @@ -1,194 +1,181 @@ -import React from 'react' -import {shallow} from 'enzyme' -import {Table, TableRow, TableCell, LazyRenderedList} from 'mailspring-component-kit' -import {testDataSource} from '../../fixtures/table-data' - +import React from 'react'; +import { shallow } from 'enzyme'; +import { Table, TableRow, TableCell, LazyRenderedList } from 'mailspring-component-kit'; +import { testDataSource } from '../../fixtures/table-data'; describe('Table Components', function describeBlock() { describe('TableCell', () => { it('renders children correctly', () => { - const element = shallow(Cell) - expect(element.text()).toEqual('Cell') + const element = shallow(Cell); + expect(element.text()).toEqual('Cell'); }); it('renders a th when is header', () => { - const element = shallow() - expect(element.type()).toEqual('th') + const element = shallow(); + expect(element.type()).toEqual('th'); }); it('renders a td when is not header', () => { - const element = shallow() - expect(element.type()).toEqual('td') + const element = shallow(); + expect(element.type()).toEqual('td'); }); it('renders extra classNames', () => { - const element = shallow() - expect(element.hasClass('my-cell')).toBe(true) + const element = shallow(); + expect(element.hasClass('my-cell')).toBe(true); }); it('passes additional props to cell', () => { - const handler = () => {} - const element = shallow() - expect(element.prop('onClick')).toBe(handler) + const handler = () => {}; + const element = shallow(); + expect(element.prop('onClick')).toBe(handler); }); }); describe('TableRow', () => { function renderRow(props = {}) { - return shallow( - - ) + return shallow(); } it('renders extra classNames', () => { - const row = renderRow({className: 'my-row'}) - expect(row.hasClass('my-row')).toBe(true) + const row = renderRow({ className: 'my-row' }); + expect(row.hasClass('my-row')).toBe(true); }); it('renders correct className when row is header', () => { - const row = renderRow({isHeader: true}) - expect(row.hasClass('table-row-header')).toBe(true) + const row = renderRow({ isHeader: true }); + expect(row.hasClass('table-row-header')).toBe(true); }); it('renders cells correctly given the tableDataSource', () => { - const row = renderRow() - expect(row.children().length).toBe(3) + const row = renderRow(); + expect(row.children().length).toBe(3); row.children().forEach((cell, idx) => { - expect(cell.type()).toBe(TableCell) - expect(cell.childAt(0).text()).toEqual(`${idx + 1}`) - }) + expect(cell.type()).toBe(TableCell); + expect(cell.childAt(0).text()).toEqual(`${idx + 1}`); + }); }); it('renders cells correctly if row is header', () => { - const row = renderRow({isHeader: true, rowIdx: null}) - expect(row.children().length).toBe(3) + const row = renderRow({ isHeader: true, rowIdx: null }); + expect(row.children().length).toBe(3); row.children().forEach((cell, idx) => { - expect(cell.type()).toBe(TableCell) - expect(cell.childAt(0).text()).toEqual(`col${idx + 1}`) - }) - }); - - it('renders an empty first cell if displayNumbers is specified and is header', () => { - const row = renderRow({displayNumbers: true, isHeader: true, rowIdx: null}) - const cell = row.childAt(0) - expect(row.children().length).toBe(4) - expect(cell.type()).toBe(TableCell) - expect(cell.hasClass('numbered-cell')).toBe(true) - expect(cell.childAt(0).text()).toEqual('') + expect(cell.type()).toBe(TableCell); + expect(cell.childAt(0).text()).toEqual(`col${idx + 1}`); + }); }); it('renders first cell with row number if displayNumbers specified', () => { - const row = renderRow({displayNumbers: true}) - expect(row.children().length).toBe(4) + const row = renderRow({ displayNumbers: true }); + expect(row.children().length).toBe(4); - const cell = row.childAt(0) - expect(cell.type()).toBe(TableCell) - expect(cell.hasClass('numbered-cell')).toBe(true) - expect(cell.childAt(0).text()).toEqual('1') + const cell = row.childAt(0); + expect(cell.type()).toBe(TableCell); + expect(cell.hasClass('numbered-cell')).toBe(true); + expect(cell.childAt(0).text()).toEqual('1'); }); it('renders cell correctly given the CellRenderer', () => { - const CellRenderer = (props) =>
- const row = renderRow({CellRenderer}) - expect(row.children().length).toBe(3) - row.children().forEach((cell) => { - expect(cell.type()).toBe(CellRenderer) - }) + const CellRenderer = props =>
; + const row = renderRow({ CellRenderer }); + expect(row.children().length).toBe(3); + row.children().forEach(cell => { + expect(cell.type()).toBe(CellRenderer); + }); }); it('passes correct props to children cells', () => { - const extraProps = {prop1: 'prop1'} - const row = renderRow({extraProps}) - expect(row.children().length).toBe(3) + const extraProps = { prop1: 'prop1' }; + const row = renderRow({ extraProps }); + expect(row.children().length).toBe(3); row.children().forEach((cell, idx) => { - expect(cell.type()).toBe(TableCell) - expect(cell.prop('rowIdx')).toEqual(0) - expect(cell.prop('colIdx')).toEqual(idx) - expect(cell.prop('prop1')).toEqual('prop1') - expect(cell.prop('tableDataSource')).toBe(testDataSource) - }) + expect(cell.type()).toBe(TableCell); + expect(cell.prop('rowIdx')).toEqual(0); + expect(cell.prop('colIdx')).toEqual(idx); + expect(cell.prop('prop1')).toEqual('prop1'); + expect(cell.prop('tableDataSource')).toBe(testDataSource); + }); }); }); describe('Table', () => { function renderTable(props = {}) { - return shallow() + return shallow(
); } - it('renders extra classNames', () => { - const table = renderTable({className: 'my-table'}) - expect(table.hasClass('mailspring-table')).toBe(true) - expect(table.hasClass('my-table')).toBe(true) - }); - describe('renderHeader', () => { it('renders nothing if displayHeader is not specified', () => { - const table = renderTable({displayHeader: false}) - expect(table.find('thead').length).toBe(0) + const table = renderTable({ displayHeader: false }); + expect(table.find('thead').length).toBe(0); }); it('renders header row with the given RowRenderer', () => { - const RowRenderer = (props) =>
- const table = renderTable({displayHeader: true, RowRenderer}) - const header = table.find('thead').childAt(0) - expect(header.type()).toBe(RowRenderer) + const RowRenderer = props =>
; + const table = renderTable({ displayHeader: true, RowRenderer }); + const header = table.find('thead').childAt(0); + expect(header.type()).toBe(RowRenderer); }); it('passes correct props to header row', () => { - const table = renderTable({displayHeader: true, displayNumbers: true, extraProps: {p1: 'p1'}}) - const header = table.find('thead').childAt(0) - expect(header.type()).toBe(TableRow) - expect(header.prop('rowIdx')).toBe(null) - expect(header.prop('tableDataSource')).toBe(testDataSource) - expect(header.prop('displayNumbers')).toBe(true) - expect(header.prop('isHeader')).toBe(true) - expect(header.prop('p1')).toEqual('p1') - expect(header.prop('extraProps')).toEqual({isHeader: true, p1: 'p1'}) + const table = renderTable({ + displayHeader: true, + displayNumbers: true, + extraProps: { p1: 'p1' }, + }); + const header = table.find('thead').childAt(0); + expect(header.type()).toBe(TableRow); + expect(header.prop('rowIdx')).toBe(null); + expect(header.prop('tableDataSource')).toBe(testDataSource); + expect(header.prop('displayNumbers')).toBe(true); + expect(header.prop('isHeader')).toBe(true); + expect(header.prop('p1')).toEqual('p1'); + expect(header.prop('extraProps')).toEqual({ isHeader: true, p1: 'p1' }); }); }); describe('renderBody', () => { it('renders a lazy list with correct rows when header should not be displayed', () => { - const table = renderTable() - const body = table.find(LazyRenderedList) - expect(body.prop('items')).toEqual(testDataSource.rows()) - expect(body.prop('BufferTag')).toEqual('tr') - expect(body.prop('RootRenderer')).toEqual('tbody') + const table = renderTable(); + const body = table.find(LazyRenderedList); + expect(body.prop('items')).toEqual(testDataSource.rows()); + expect(body.prop('BufferTag')).toEqual('tr'); + expect(body.prop('RootRenderer')).toEqual('tbody'); }); }); describe('renderRow', () => { it('renders row with the given RowRenderer', () => { - const RowRenderer = (props) =>
- const table = renderTable({RowRenderer}) - const Renderer = table.instance().renderRow - const row = shallow() - expect(row.type()).toBe(RowRenderer) + const RowRenderer = props =>
; + const table = renderTable({ RowRenderer }); + const Renderer = table.instance().renderRow; + const row = shallow(); + expect(row.type()).toBe(RowRenderer); }); it('passes the correct props to the row when displayHeader is true', () => { - const CellRenderer = (props) =>
- const extraProps = {p1: 'p1'} - const table = renderTable({displayHeader: true, displayNumbers: true, extraProps, CellRenderer}) - const Renderer = table.instance().renderRow - const row = shallow() - expect(row.prop('p1')).toEqual('p1') - expect(row.prop('rowIdx')).toBe(5) - expect(row.prop('displayNumbers')).toBe(true) - expect(row.prop('tableDataSource')).toBe(testDataSource) - expect(row.prop('extraProps')).toBe(extraProps) - expect(row.prop('CellRenderer')).toBe(CellRenderer) + const CellRenderer = props =>
; + const extraProps = { p1: 'p1' }; + const table = renderTable({ + displayHeader: true, + displayNumbers: true, + extraProps, + CellRenderer, + }); + const Renderer = table.instance().renderRow; + const row = shallow(); + expect(row.prop('p1')).toEqual('p1'); + expect(row.prop('rowIdx')).toBe(5); + expect(row.prop('displayNumbers')).toBe(true); + expect(row.prop('tableDataSource')).toBe(testDataSource); + expect(row.prop('extraProps')).toBe(extraProps); + expect(row.prop('CellRenderer')).toBe(CellRenderer); }); it('passes the correct props to the row when displayHeader is false', () => { - const table = renderTable({displayHeader: false}) - const Renderer = table.instance().renderRow - const row = shallow() - expect(row.prop('rowIdx')).toBe(5) + const table = renderTable({ displayHeader: false }); + const Renderer = table.instance().renderRow; + const row = shallow(); + expect(row.prop('rowIdx')).toBe(5); }); }); }); diff --git a/app/spec/components/tokenizing-text-field-spec.tsx b/app/spec/components/tokenizing-text-field-spec.tsx index 7e6a2e71d..6bbebc635 100644 --- a/app/spec/components/tokenizing-text-field-spec.tsx +++ b/app/spec/components/tokenizing-text-field-spec.tsx @@ -1,8 +1,8 @@ import React from 'react'; const { mount } = require('enzyme'); -import { Contact } from 'mailspring-exports';; -import { KeyCommandsRegion, TokenizingTextField, Menu } from 'mailspring-component-kit';; +import { Contact } from 'mailspring-exports'; +import { KeyCommandsRegion, TokenizingTextField, Menu } from 'mailspring-component-kit'; class CustomToken extends React.Component { render() { @@ -404,14 +404,15 @@ describe('TokenizingTextField.Token', function() { this.propEdit = jasmine.createSpy('onEdit'); this.propClick = jasmine.createSpy('onClick'); this.token = mount( - React.createElement(TokenizingTextField.Token, { - selected: false, - valid: true, - item: participant1, - onClick: this.propClick, - onEdited: this.propEdit, - onDragStart: jasmine.createSpy('onDragStart'), - }) + ); }); @@ -424,8 +425,10 @@ describe('TokenizingTextField.Token', function() { it('should call onEdit to commit the new token value when the edit field is blurred', function() { expect(this.token.state().editing).toBe(false); this.token.simulate('doubleClick', {}); + expect(this.token.state().editing).toBe(true); const tokenEditInput = this.token.find('input'); - tokenEditInput.simulate('change', { target: { value: 'new tag content' } }); + tokenEditInput.getDOMNode().value = 'new tag content'; + tokenEditInput.simulate('change'); tokenEditInput.simulate('blur'); expect(this.propEdit).toHaveBeenCalledWith(participant1, 'new tag content'); }); diff --git a/app/spec/models/event-spec.ts b/app/spec/models/event-spec.ts deleted file mode 100644 index 4f43f3680..000000000 --- a/app/spec/models/event-spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Event } from '../../src/flux/models/event'; - -const json_event = { - __cls: 'Event', - id: '4ee4xbnx7pxdb9g7c2f8ncyto', - calendar_id: 'ci0k1wfyv533ccgox4t7uri4h', - account_id: '14e5bn96uizyuhidhcw5rfrb0', - description: null, - location: null, - participants: [ - { - email: 'example@gmail.com', - name: 'Ben Bitdiddle', - status: 'yes', - }, - ], - read_only: false, - title: 'Meeting with Ben Bitdiddle', - when: { - object: 'timespan', - end_time: 1408123800, - start_time: 1408120200, - }, - busy: true, - status: 'confirmed', -}; - -const when_1 = { - end_time: 1408123800, - start_time: 1408120200, -}; - -const participant_1 = { - name: 'Ethan Blackburn', - status: 'yes', - email: 'ethan@mailspring.com', -}; - -const participant_2 = { - name: 'Other Person', - status: 'maybe', - email: 'other@person.com', -}; - -const participant_3 = { - name: 'Another Person', - status: 'no', - email: 'another@person.com', -}; - -const event_1 = { - title: 'Dolores', - description: 'Hanging at the park', - location: 'Dolores Park', - when: when_1, - start: 1408120200, - end: 1408123800, - participants: [participant_1, participant_2, participant_3], -}; - -describe('Event', function() { - it('can be built via the constructor', function() { - const e1 = new Event(event_1); - expect(e1.title).toBe('Dolores'); - expect(e1.description).toBe('Hanging at the park'); - expect(e1.location).toBe('Dolores Park'); - expect(e1.when.start_time).toBe(1408120200); - expect(e1.when.end_time).toBe(1408123800); - expect(e1.start).toBe(1408120200); - expect(e1.end).toBe(1408123800); - expect(e1.participants[0].name).toBe('Ethan Blackburn'); - expect(e1.participants[0].email).toBe('ethan@mailspring.com'); - expect(e1.participants[0].status).toBe('yes'); - expect(e1.participants[1].name).toBe('Other Person'); - expect(e1.participants[1].email).toBe('other@person.com'); - expect(e1.participants[1].status).toBe('maybe'); - expect(e1.participants[2].name).toBe('Another Person'); - expect(e1.participants[2].email).toBe('another@person.com'); - expect(e1.participants[2].status).toBe('no'); - }); - - it('accepts a JSON response', function() { - const e1 = new Event().fromJSON(json_event); - expect(e1.title).toBe('Meeting with Ben Bitdiddle'); - expect(e1.description).toBe(null); - expect(e1.location).toBe(null); - expect(e1.start).toBe(1408120200); - expect(e1.end).toBe(1408123800); - expect(e1.participants[0].name).toBe('Ben Bitdiddle'); - expect(e1.participants[0].email).toBe('example@gmail.com'); - expect(e1.participants[0].status).toBe('yes'); - }); -}); diff --git a/app/spec/spec-runner/gui-reporter.tsx b/app/spec/spec-runner/gui-reporter.tsx index c699af2de..ff7eaa444 100644 --- a/app/spec/spec-runner/gui-reporter.tsx +++ b/app/spec/spec-runner/gui-reporter.tsx @@ -98,12 +98,8 @@ class N1GuiReporter extends React.Component {
{this._renderSpecsOfType('core')}
-
Bundled
-
{this._renderSpecsOfType('bundled')}
-
-
-
User
-
{this._renderSpecsOfType('user')}
+
Packages
+
{this._renderSpecsOfType(undefined)}
{this._renderStatus()}
{this._renderFailures()}
diff --git a/app/spec/spellchecker-spec.ts b/app/spec/spellchecker-spec.ts index 856d4de66..c1bd7924d 100644 --- a/app/spec/spellchecker-spec.ts +++ b/app/spec/spellchecker-spec.ts @@ -17,13 +17,13 @@ describe('Spellchecker', function spellcheckerTests() { }); // Apparently handleElectronSpellCheck returns !misspelled spyOn(Spellchecker.handler, 'handleElectronSpellCheck').andReturn(false); - Spellchecker.isMisspelledCache = {}; + Spellchecker.handler['isMisspelledCache'].reset(); }); it('does not call spellchecker when word has already been learned', () => { - Spellchecker.isMisspelledCache = { mispelled: true }; - const misspelled = Spellchecker.isMisspelled('mispelled'); - expect(misspelled).toBe(true); + Spellchecker.learnWord('mispaelled'); + const misspelled = Spellchecker.isMisspelled('mispaelled'); + expect(misspelled).toBe(false); expect(Spellchecker.handler.handleElectronSpellCheck).not.toHaveBeenCalled(); }); diff --git a/app/spec/stores/draft-factory-spec.ts b/app/spec/stores/draft-factory-spec.ts index 3a49c6757..4135222a3 100644 --- a/app/spec/stores/draft-factory-spec.ts +++ b/app/spec/stores/draft-factory-spec.ts @@ -453,7 +453,7 @@ describe('DraftFactory', function draftFactory() { expect(this.model.body.indexOf('gmail_quote') > 0).toBe(true); expect(this.model.body.indexOf('blockquote') > 0).toBe(false); expect(this.model.body.indexOf(fakeMessage1.body) > 0).toBe(true); - expect(this.model.body.indexOf('---------- Forwarded message ---------') > 0).toBe(true); + expect(this.model.body.indexOf('---------- Forwarded Message ---------') > 0).toBe(true); expect(this.model.body.indexOf('From: Customer <customer@example.com>') > 0).toBe( true ); @@ -624,7 +624,7 @@ describe('DraftFactory', function draftFactory() { describe('when there is not an existing draft at the bottom of the thread', () => { beforeEach(() => { - spyOn(DatabaseStore, 'run').andCallFake(() => [fakeMessage1]); + spyOn(DatabaseStore, 'run').andCallFake(async () => [fakeMessage1]); spyOn(DraftFactory, 'createDraftForReply'); }); @@ -639,7 +639,6 @@ describe('DraftFactory', function draftFactory() { thread: fakeThread, message: fakeMessage1, type: 'reply-all', - behavior: 'prefer-existing', }); await DraftFactory.createOrUpdateDraftForReply({ @@ -652,7 +651,6 @@ describe('DraftFactory', function draftFactory() { thread: fakeThread, message: fakeMessage1, type: 'reply', - behavior: 'prefer-existing', }); }); }); diff --git a/app/spec/stores/feature-usage-store-spec.ts b/app/spec/stores/feature-usage-store-spec.ts index 59055b70c..bbf6cfd4c 100644 --- a/app/spec/stores/feature-usage-store-spec.ts +++ b/app/spec/stores/feature-usage-store-spec.ts @@ -109,8 +109,7 @@ describe('FeatureUsageStore', function featureUsageStoreSpec() { modalClass: 'not-usable', headerText: 'all test used', iconUrl: 'icon url', - rechargeText: - 'You can add a test to 10 emails a month with Mailspring Basic. Upgrade to Pro today!', + rechargeText: 'add a test to', }); }); diff --git a/app/spec/stores/identity-store-spec.ts b/app/spec/stores/identity-store-spec.ts index 428b3a23e..b90c1433f 100644 --- a/app/spec/stores/identity-store-spec.ts +++ b/app/spec/stores/identity-store-spec.ts @@ -32,7 +32,7 @@ describe('IdentityStore', function identityStoreSpec() { }); it('clears passwords if unsetting', async () => { - IdentityStore.saveIdentity(null); + await IdentityStore.saveIdentity(null); expect(KeyManager.deletePassword).toHaveBeenCalled(); expect(KeyManager.replacePassword).not.toHaveBeenCalled(); expect(AppEnv.config.set).toHaveBeenCalled(); @@ -46,7 +46,7 @@ describe('IdentityStore', function identityStoreSpec() { const next = JSON.parse(JSON.stringify(this.identityJSON)); next.featureUsage.feat.usedInPeriod += 1; - IdentityStore.saveIdentity(next); + await IdentityStore.saveIdentity(next); expect(used()).toBe(2); }); }); diff --git a/app/src/components/date-input.tsx b/app/src/components/date-input.tsx index 44d237fe9..e390a8e24 100644 --- a/app/src/components/date-input.tsx +++ b/app/src/components/date-input.tsx @@ -9,6 +9,7 @@ type DateInputProps = { dateFormat: string; onDateInterpreted?: (...args: any[]) => any; onDateSubmitted?: (...args: any[]) => any; + initialTestState?: DateInputState; }; type DateInputState = { inputValue: string; inputDate: null } & any; @@ -46,7 +47,10 @@ class DateInput extends Component { } onInputKeyDown = event => { - const { key, target: { value } } = event; + const { + key, + target: { value }, + } = event; if (value.length > 0 && ['Enter', 'Return'].includes(key)) { // This prevents onInputChange from being fired event.stopPropagation(); @@ -56,7 +60,9 @@ class DateInput extends Component { }; onInputChange = event => { - const { target: { value } } = event; + const { + target: { value }, + } = event; const nextDate = DateUtils.futureDateFromString(value); if (nextDate) { this.props.onDateInterpreted(nextDate.clone(), value);