import React from 'react'; import { mount } from 'enzyme'; import { ContactStore, Contact } from 'mailspring-exports'; import { ParticipantsTextField } from 'mailspring-component-kit'; const participant1 = new Contact({ id: 'local-1', email: 'ben@nylas.com', }); const participant2 = new Contact({ id: 'local-2', email: 'ben@example.com', name: 'Ben Gotow', }); const participant3 = new Contact({ id: 'local-3', email: 'evan@nylas.com', name: 'Evan Morikawa', }); xdescribe('ParticipantsTextField', function ParticipantsTextFieldSpecs() { beforeEach(() => { spyOn(AppEnv, 'isMainWindow').andReturn(true); this.propChange = jasmine.createSpy('change'); this.fieldName = 'to'; this.participants = { to: [participant1, participant2], cc: [participant3], bcc: [], }; this.renderedField = mount( ); this.renderedInput = this.renderedField.find('input'); this.expectInputToYield = (input, expected) => { const reviver = function reviver(k, v) { if (k === 'id' || k === 'client_id' || k === 'server_id' || k === 'object') { return undefined; } return v; }; runs(() => { this.renderedInput.simulate('change', { target: { value: input } }); advanceClock(100); return this.renderedInput.simulate('keyDown', { key: 'Enter', keyCode: 9 }); }); waitsFor(() => { return this.propChange.calls.length > 0; }); runs(() => { let found = this.propChange.mostRecentCall.args[0]; found = JSON.parse(JSON.stringify(found), reviver); expect(found).toEqual(JSON.parse(JSON.stringify(expected), reviver)); // This advance clock needs to be here because our waitsFor latch // catches the first time that propChange gets called. More stuff // may happen after this and we need to advance the clock to // "clear" all of that. If we don't do this it throws errors about // `setState` being called on unmounted components :( return advanceClock(100); }); }; }); it('renders into the document', () => { expect(this.renderedField.find(ParticipantsTextField).length).toBe(1); }); describe('inserting participant text', () => { it('should fire onChange with an updated participants hash', () => { this.expectInputToYield('abc@abc.com', { to: [ participant1, participant2, new Contact({ name: 'abc@abc.com', email: 'abc@abc.com' }), ], cc: [participant3], bcc: [], }); }); it('should remove added participants from other fields', () => { this.expectInputToYield(participant3.email, { to: [ participant1, participant2, new Contact({ name: participant3.email, email: participant3.email }), ], cc: [], bcc: [], }); }); it('should use the name of an existing contact in the ContactStore if possible', () => { spyOn(ContactStore, 'searchContacts').andCallFake(val => { if (val === participant3.name) { return Promise.resolve([participant3]); } return Promise.resolve([]); }); this.expectInputToYield(participant3.name, { to: [participant1, participant2, participant3], cc: [], bcc: [], }); }); it("should use the plain email if that's what's entered", () => { spyOn(ContactStore, 'searchContacts').andCallFake(val => { if (val === participant3.name) { return Promise.resolve([participant3]); } return Promise.resolve([]); }); this.expectInputToYield(participant3.email, { to: [participant1, participant2, new Contact({ email: 'evan@nylas.com' })], cc: [], bcc: [], }); }); it('should not have the same contact auto-picked multiple times', () => { spyOn(ContactStore, 'searchContacts').andCallFake(val => { if (val === participant2.name) { return Promise.resolve([participant2]); } return Promise.resolve([]); }); this.expectInputToYield(participant2.name, { to: [ participant1, participant2, new Contact({ email: participant2.name, name: participant2.name }), ], cc: [participant3], bcc: [], }); }); describe('when text contains Name (Email) formatted data', () => { it('should correctly parse it into named Contact objects', () => { const newContact1 = new Contact({ id: 'b1', name: 'Ben Imposter', email: 'imposter@nylas.com', }); const newContact2 = new Contact({ name: 'Nylas Team', email: 'feedback@nylas.com' }); const inputs = [ 'Ben Imposter , Nylas Team ', '\n\nbla\nBen Imposter (imposter@nylas.com), Nylas Team (feedback@nylas.com)', 'Hello world! I like cheese. \rBen Imposter (imposter@nylas.com)\nNylas Team (feedback@nylas.com)', 'Ben ImposterNylas Team (feedback@nylas.com)', ]; for (const input of inputs) { this.expectInputToYield(input, { to: [participant1, participant2, newContact1, newContact2], cc: [participant3], bcc: [], }); } }); }); describe('when text contains emails mixed with garbage text', () => { it('should still parse out emails into Contact objects', () => { const newContact1 = new Contact({ id: 'gm', name: 'garbage-man@nylas.com', email: 'garbage-man@nylas.com', }); const newContact2 = new Contact({ id: 'rm', name: 'recycling-guy@nylas.com', email: 'recycling-guy@nylas.com', }); const inputs = [ "Hello world I real. \n asd. garbage-man@nylas.com—he's cool Also 'recycling-guy@nylas.com'!", 'garbage-man@nylas.com1WHOA I REALLY HATE DATA,recycling-guy@nylas.com', 'nils.com garbage-man@nylas.com @nylas.com nope@.com nope! recycling-guy@nylas.com HOLLA AT recycling-guy@nylas.', ]; for (const input of inputs) { this.expectInputToYield(input, { to: [participant1, participant2, newContact1, newContact2], cc: [participant3], bcc: [], }); } }); }); }); });