Mailspring/spec-inbox/components/tokenizing-text-field-spec.cjsx
Ben Gotow e4889b390f refactor(participants): Use DragDropMixin, Menu
Summary:
This diff replaces the participant text fields with ones based on TokenizingTextField, a new core
component that handles autocompletion, drag and drop of tokens, etc.

Fix large queries overloading SQLite size limits

New general purpose tokenized text field with token selection, copy paste, etc

Move pre-send logic to the view since DraftStore requests from db, which may not have settled

Tests - still a WIP

Support for contextual menus instead of X

Test Plan: Run new tests. Needs many more, but have higher priority things to fix

Reviewers: evan

Reviewed By: evan

Differential Revision: https://review.inboxapp.com/D1142
2015-02-06 14:46:30 -08:00

162 lines
6.4 KiB
CoffeeScript

_ = require 'underscore-plus'
CSON = require 'season'
React = require 'react/addons'
ReactTestUtils = React.addons.TestUtils
{InboxTestUtils,
Namespace,
NamespaceStore,
Contact,
} = require 'inbox-exports'
{TokenizingTextField} = require 'ui-components'
me = new Namespace
name: 'Test User'
email: 'test@example.com'
provider: 'inbox'
NamespaceStore._current = me
CustomToken = React.createClass
render: ->
<span>{@props.item.email}</span>
CustomSuggestion = React.createClass
render: ->
<span>{@props.item.email}</span>
participant1 = new Contact
email: 'ben@nilas.com'
participant2 = new Contact
email: 'ben@example.com'
name: 'ben'
participant3 = new Contact
email: 'ben@inboxapp.com'
name: 'Duplicate email'
participant4 = new Contact
email: 'ben@elsewhere.com',
name: 'ben again'
participant5 = new Contact
email: 'evan@elsewhere.com',
name: 'EVAN'
fdescribe 'TokenizingTextField', ->
keymap_path = 'keymaps/base.cson'
keymap_file = CSON.readFileSync(keymap_path)
atom.keymaps.add(keymap_path, keymap_file)
beforeEach ->
@completions = []
@propAdd = jasmine.createSpy 'add'
@propRemove = jasmine.createSpy 'remove'
@propTokenKey = (p) -> p.email
@propTokenContent = (p) -> <CustomToken item={p} />
@propCompletionsForInput = (input) => @completions
@propCompletionContent = (p) -> <CustomSuggestion item={p} />
spyOn(@, 'propCompletionContent').andCallThrough()
spyOn(@, 'propCompletionsForInput').andCallThrough()
@fieldName = 'to'
@tabIndex = 100
@tokens = [participant1, participant2, participant3]
@renderedField = ReactTestUtils.renderIntoDocument(
<TokenizingTextField
name={@fieldName}
tabIndex={@tabIndex}
tokens={@tokens}
tokenKey={@propTokenKey}
tokenContent={@propTokenContent}
completionsForInput={@propCompletionsForInput}
completionContent={@propCompletionContent}
add={@propAdd}
remove={@propRemove} />
)
@renderedInput = ReactTestUtils.findRenderedDOMComponentWithTag(@renderedField, 'input').getDOMNode()
it 'renders into the document', ->
expect(ReactTestUtils.isCompositeComponentWithType @renderedField, TokenizingTextField).toBe(true)
it 'applies the tabIndex provided to the inner input', ->
expect(@renderedInput.tabIndex).toBe(@tabIndex)
it 'shows the tokens provided by the tokenContent method', ->
@renderedTokens = ReactTestUtils.scryRenderedComponentsWithType(@renderedField, CustomToken)
expect(@renderedTokens.length).toBe(@tokens.length)
it 'shows the tokens in the correct order', ->
@renderedTokens = ReactTestUtils.scryRenderedComponentsWithType(@renderedField, CustomToken)
for i in [0..@tokens.length-1]
expect(@renderedTokens[i].props.item).toBe(@tokens[i])
describe "when focused", ->
it 'should receive the `focused` class', ->
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(@renderedField, 'focused').length).toBe(0)
ReactTestUtils.Simulate.focus(@renderedInput)
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(@renderedField, 'focused').length).toBe(1)
describe "when the user types in the input", ->
it 'should fetch completions for the text', ->
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
expect(@propCompletionsForInput).toHaveBeenCalledWith('abc')
it 'should display the completions', ->
@completions = [participant4, participant5]
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
components = ReactTestUtils.scryRenderedComponentsWithType(@renderedField, CustomSuggestion)
expect(components.length).toBe(2)
expect(components[0].props.item).toBe(participant4)
expect(components[1].props.item).toBe(participant5)
it 'should not display items with keys matching items already in the token field', ->
@completions = [participant2, participant4, participant1]
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
components = ReactTestUtils.scryRenderedComponentsWithType(@renderedField, CustomSuggestion)
expect(components.length).toBe(1)
expect(components[0].props.item).toBe(participant4)
['enter', ','].forEach (key) ->
describe "when the user presses #{key}", ->
describe "and there is an completion available", ->
it "should call add with the first completion", ->
@completions = [participant4]
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
InboxTestUtils.keyPress(key, @renderedInput)
expect(@propAdd).toHaveBeenCalledWith(participant4)
describe "and there is NO completion available", ->
it 'should call add, allowing the parent to (optionally) turn the text into a token', ->
@completions = []
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
InboxTestUtils.keyPress(key, @renderedInput)
expect(@propAdd).toHaveBeenCalledWith('abc')
describe "when the user presses tab", ->
describe "and there is an completion available", ->
it "should call add with the first completion", ->
@completions = [participant4]
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'abc'}})
InboxTestUtils.keyPress('tab', @renderedInput)
expect(@propAdd).toHaveBeenCalledWith(participant4)
describe "when blurred", ->
it 'should call add, allowing the parent component to (optionally) turn the entered text into a token', ->
ReactTestUtils.Simulate.focus(@renderedInput)
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'text'}})
ReactTestUtils.Simulate.blur(@renderedInput)
expect(@propAdd).toHaveBeenCalledWith('text')
it 'should clear the entered text', ->
ReactTestUtils.Simulate.focus(@renderedInput)
ReactTestUtils.Simulate.change(@renderedInput, {target: {value: 'text'}})
ReactTestUtils.Simulate.blur(@renderedInput)
expect(@renderedInput.value).toBe('')
it 'should no longer have the `focused` class', ->
ReactTestUtils.Simulate.focus(@renderedInput)
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(@renderedField, 'focused').length).toBe(1)
ReactTestUtils.Simulate.blur(@renderedInput)
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(@renderedField, 'focused').length).toBe(0)