Mailspring/packages/local-sync/spec/message-factory-spec.js
Christine Spang 40c7b09e27 [local-sync] Use mimelib to parse contacts
Summary:
Because of the way we were attempting to parse contacts from
From/To/Cc/Bcc headers by converting them to JSON with a regex, we were
erroneously breaking contacts that contained commas in quoted names into
multiple contacts. This could result in things like parsing multiple
addresses for the From: header, incorrectly!

To resolve the problem, replace our homegrown logic with mimelib's
seemingly excellent parseAddresses(), which handles this and a myriad of
other cases correctly.

Fixes: T7370

Test Plan: unit tests included

Reviewers: mark

Reviewed By: mark

Differential Revision: https://phab.nylas.com/D3565
2016-12-29 15:48:12 -08:00

128 lines
5.5 KiB
JavaScript

const LocalDatabaseConnector = require('../src/shared/local-database-connector');
const {parseFromImap, extractSnippet, extractContacts} = require('../src/shared/message-factory');
const {forEachJSONFixture, forEachHTMLAndTXTFixture, ACCOUNT_ID} = require('./helpers');
xdescribe('MessageFactory', function MessageFactorySpecs() {
beforeEach(() => {
waitsForPromise(async () => {
await LocalDatabaseConnector.ensureAccountDatabase(ACCOUNT_ID);
const db = await LocalDatabaseConnector.forAccount(ACCOUNT_ID);
const folder = await db.Folder.create({
id: 'test-folder-id',
accountId: ACCOUNT_ID,
version: 1,
name: 'Test Folder',
role: null,
});
this.options = { accountId: ACCOUNT_ID, db, folder };
})
})
afterEach(() => {
LocalDatabaseConnector.destroyAccountDatabase(ACCOUNT_ID)
})
describe("parseFromImap", () => {
forEachJSONFixture('MessageFactory/parseFromImap', (filename, json) => {
it(`should correctly build message properties for ${filename}`, () => {
const {imapMessage, desiredParts, result} = json;
// requiring these to match makes it overly arduous to generate test
// cases from real accounts
const excludeKeys = new Set(['id', 'accountId', 'folderId', 'folder', 'labels']);
waitsForPromise(async () => {
const actual = await parseFromImap(imapMessage, desiredParts, this.options);
for (const key of Object.keys(result)) {
if (!excludeKeys.has(key)) {
expect(actual[key]).toEqual(result[key]);
}
}
});
});
})
});
});
const snippetTestCases = [{
purpose: 'trim whitespace in basic plaintext',
plainBody: 'The quick brown fox\n\n\tjumps over the lazy',
htmlBody: null,
snippet: 'The quick brown fox jumps over the lazy',
}, {
purpose: 'truncate long plaintext without breaking words',
plainBody: 'The quick brown fox jumps over the lazy dog and then the lazy dog rolls over and sighs. The fox turns around in a circle and then jumps onto a bush! It grins wickedly and wags its fat tail. As the lazy dog puts its head on its paws and cracks a sleepy eye open, a slow grin forms on its face. The fox has fallen into the bush and is yelping and squeaking.',
htmlBody: null,
snippet: 'The quick brown fox jumps over the lazy dog and then the lazy dog rolls over and sighs. The fox turns',
}, {
purpose: 'prefer HTML to plaintext, and process basic HTML correctly',
plainBody: 'This email would look TOTES AMAZING if your silly mail client supported HTML.',
htmlBody: '<html><title>All About Ponies</title><h1>PONIES AND RAINBOWS AND UNICORNS</h1><p>Unicorns are native to the hillsides of Flatagonia.</p></html>',
snippet: 'PONIES AND RAINBOWS AND UNICORNS Unicorns are native to the hillsides of Flatagonia.',
}, {
purpose: 'properly strip rogue styling inside of <body> and trim whitespace in HTML',
plainBody: null,
htmlBody: '<html>\n <head></head>\n <body>\n <style>\n body { width: 100% !important; min-width: 100%; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; margin: 0; padding: 0; background: #fafafa;\n </style>\n <p>Look ma, no CSS!</p></body></html>',
snippet: 'Look ma, no CSS!',
}, {
purpose: 'properly process <br/> and <div/>',
plainBody: null,
htmlBody: '<p>Unicorns are <div>native</div>to the<br/>hillsides of<br/>Flatagonia.</p>',
snippet: 'Unicorns are native to the hillsides of Flatagonia.',
}, {
purpose: 'properly strip out HTML comments',
plainBody: null,
htmlBody: '<p>Unicorns are<!-- an HTML comment! -->native to the</p>',
snippet: 'Unicorns are native to the',
}, {
purpose: "don't add extraneous spaces after text format markup",
plainBody: null,
htmlBody: `
<td style="padding: 0px 10px">
Hey there, <b>Nylas</b>!<br>
You have a new follower on Product Hunt.
</td>`,
snippet: 'Hey there, Nylas! You have a new follower on Product Hunt.',
},
]
const contactsTestCases = [{
purpose: "not erroneously split contact names on commas",
// NOTE: inputs must be in same format as output by mimelib.parseHeader
input: ['"Little Bo Peep, The Hill" <bopeep@example.com>'],
output: [{name: "Little Bo Peep, The Hill", email: "bopeep@example.com"}],
}, {
purpose: "extract two separate contacts, removing quotes properly & respecing unicode",
input: ['AppleBees Zé <a@example.com>, "Tiger Zen" b@example.com'],
output: [
{name: 'AppleBees Zé', email: 'a@example.com'},
{name: 'Tiger Zen', email: 'b@example.com'},
],
},
]
describe('MessageFactoryHelpers', function MessageFactoryHelperSpecs() {
describe('extractSnippet (basic)', () => {
snippetTestCases.forEach(({purpose, plainBody, htmlBody, snippet}) => {
it(`should ${purpose}`, () => {
const parsedSnippet = extractSnippet(plainBody, htmlBody);
expect(parsedSnippet).toEqual(snippet);
});
});
});
describe('extractSnippet (real world)', () => {
forEachHTMLAndTXTFixture('MessageFactory/extractSnippet', (filename, html, txt) => {
it(`should correctly extract the snippet from the html`, () => {
const parsedSnippet = extractSnippet(null, html);
expect(parsedSnippet).toEqual(txt);
});
});
});
describe('extractContacts (basic)', () => {
contactsTestCases.forEach(({purpose, input, output}) => {
it(`should ${purpose}`, () => {
const parsedContacts = extractContacts(input);
expect(parsedContacts).toEqual(output);
});
});
});
});