fix(lint): Various linter fixes

This commit is contained in:
Ben Gotow 2016-05-06 16:23:48 -07:00
parent 7533dc0ac6
commit 2ff92946b7
23 changed files with 184 additions and 305 deletions

View file

@ -5,7 +5,7 @@ const EmojiActions = Reflux.createActions([
"useEmoji",
]);
for (const key in EmojiActions) {
for (const key of Object.keys(EmojiActions)) {
EmojiActions[key].sync = true;
}

View file

@ -1,3 +1,4 @@
/* eslint global-require: 0 */
import {PreferencesUIStore, ComponentRegistry, ExtensionRegistry} from 'nylas-exports';
import TemplatePicker from './template-picker';
import TemplateStatusBar from './template-status-bar';

View file

@ -10,12 +10,12 @@ const Fields = {
Fields.ParticipantFields = [Fields.To, Fields.Cc, Fields.Bcc];
Fields.Order = {
"textFieldTo": 1,
"textFieldCc": 2,
"textFieldBcc": 3,
"fromField": -1, // Not selectable
"textFieldSubject": 5,
"contentBody": 6,
textFieldTo: 1,
textFieldCc: 2,
textFieldBcc: 3,
fromField: -1, // Not selectable
textFieldSubject: 5,
contentBody: 6,
};
export default Fields

View file

@ -13,9 +13,9 @@ describe('AutoloadImagesExtension', function autoloadImagesExtension() {
const name = filename.replace('-in.html', '');
scenarios.push({
name: name,
in: fs.readFileSync(path.join(fixtures, filename)).toString(),
out: fs.readFileSync(path.join(fixtures, `${name}-out.html`)).toString(),
'name': name,
'in': fs.readFileSync(path.join(fixtures, filename)).toString(),
'out': fs.readFileSync(path.join(fixtures, `${name}-out.html`)).toString(),
});
}
});

View file

@ -59,7 +59,7 @@ export function autolink(doc, {async} = {}) {
['mailto:', RegExpUtils.emailRegex(), {
// Technically, gmail.com/bengotow@gmail.com is an email address. After
// matching, manully exclude any email that follows the .*[/?].*@ pattern.
exclude: [/\..*[\/|\?].*\@/],
exclude: [/\..*[\/|\?].*@/],
}],
['tel:', RegExpUtils.phoneRegex()],
['', RegExpUtils.urlRegex({matchEntireString: false})],
@ -85,7 +85,7 @@ export function autolink(doc, {async} = {}) {
// Traverse the new DOM tree and make sure everything with an href has a title.
const aTagWalker = document.createTreeWalker(doc.body, NodeFilter.SHOW_ELEMENT, {
acceptNode: (node) =>
node.href ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
(node.href ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP)
,
});
while (aTagWalker.nextNode()) {

View file

@ -16,10 +16,6 @@ class PreferencesTabItem extends React.Component {
tabItem: React.PropTypes.instanceOf(PreferencesUIStore.TabItem).isRequired,
}
constructor() {
super();
}
_onClick = () => {
Actions.switchPreferencesTab(this.props.tabItem.tabId);
}
@ -33,18 +29,21 @@ class PreferencesTabItem extends React.Component {
const {tabId, displayName} = this.props.tabItem;
const classes = classNames({
"item": true,
"active": tabId === this.props.selection.get('tabId'),
item: true,
active: tabId === this.props.selection.get('tabId'),
});
let path = `icon-preferences-${displayName.toLowerCase().replace(" ", "-")}.png`
if (!fs.existsSync(Utils.imageNamed(path))) {
path = "icon-preferences-general.png";
}
const icon = (<RetinaImg
className="tab-icon"
name={path}
mode={RetinaImg.Mode.ContentPreserve} />);
const icon = (
<RetinaImg
className="tab-icon"
name={path}
mode={RetinaImg.Mode.ContentPreserve}
/>
);
return (
<div className={classes} onClick={this._onClick}>
@ -66,16 +65,13 @@ class PreferencesTabsBar extends React.Component {
selection: React.PropTypes.instanceOf(Immutable.Map).isRequired,
}
constructor() {
super();
}
renderTabs() {
return this.props.tabs.map((tabItem) =>
<PreferencesTabItem
key={tabItem.tabId}
tabItem={tabItem}
selection={this.props.selection} />
selection={this.props.selection}
/>
);
}

View file

@ -74,7 +74,12 @@ class ConfigSchemaItem extends React.Component {
} else if (this.props.configSchema.type === 'boolean') {
return (
<div className="item">
<input id={this.props.keyPath} type="checkbox" onChange={this._onChangeChecked} checked={ this.props.config.get(this.props.keyPath) }/>
<input
id={this.props.keyPath}
type="checkbox"
onChange={this._onChangeChecked}
checked={this.props.config.get(this.props.keyPath)}
/>
<label htmlFor={this.props.keyPath}>{this.props.configSchema.title}</label>
</div>
);

View file

@ -15,11 +15,17 @@ class PreferencesAccountList extends Component {
_renderAccountStateIcon(account) {
if (account.syncState !== "running") {
return (<div className="sync-error-icon"><RetinaImg
className="sync-error-icon"
name="ic-settings-account-error.png"
mode={RetinaImg.Mode.ContentIsMask} /></div>)
return (
<div className="sync-error-icon">
<RetinaImg
className="sync-error-icon"
name="ic-settings-account-error.png"
mode={RetinaImg.Mode.ContentIsMask}
/>
</div>
)
}
return null;
}
_renderAccount = (account) => {
@ -29,14 +35,16 @@ class PreferencesAccountList extends Component {
return (
<div
className={classnames({account: true, "sync-error": syncError})}
key={account.id} >
className={classnames({"account": true, "sync-error": syncError})}
key={account.id}
>
<Flexbox direction="row" style={{alignItems: 'middle'}}>
<div style={{textAlign: 'center'}}>
<RetinaImg
name={syncError ? "ic-settings-account-error.png" : `ic-settings-account-${account.provider}.png`}
fallback="ic-settings-account-imap.png"
mode={RetinaImg.Mode.ContentPreserve} />
mode={RetinaImg.Mode.ContentPreserve}
/>
</div>
<div style={{flex: 1, marginLeft: 10}}>
<div className="account-name">
@ -62,7 +70,8 @@ class PreferencesAccountList extends Component {
onReorderItem={this.props.onReorderAccount}
onCreateItem={this.props.onAddAccount}
onSelectItem={this.props.onSelectAccount}
onDeleteItem={this.props.onRemoveAccount} />
onDeleteItem={this.props.onRemoveAccount}
/>
</div>
);
}

View file

@ -64,10 +64,12 @@ class PreferencesAccounts extends React.Component {
onAddAccount={this._onAddAccount}
onReorderAccount={this._onReorderAccount}
onSelectAccount={this._onSelectAccount}
onRemoveAccount={this._onRemoveAccount} />
onRemoveAccount={this._onRemoveAccount}
/>
<PreferencesAccountDetails
account={this.state.selected}
onAccountUpdated={this._onAccountUpdated} />
onAccountUpdated={this._onAccountUpdated}
/>
</div>
</div>
);

View file

@ -4,49 +4,47 @@ import ConfigSchemaItem from './config-schema-item';
import WorkspaceSection from './workspace-section';
import SendingSection from './sending-section';
class PreferencesGeneral extends React.Component {
static displayName = 'PreferencesGeneral';
const PreferencesGeneral = (props) => {
return (
<div className="container-general" style={{maxWidth: 600}}>
static propTypes = {
config: React.PropTypes.object,
configSchema: React.PropTypes.object,
}
<WorkspaceSection config={props.config} configSchema={props.configSchema} />
render() {
return (
<div className="container-general" style={{maxWidth: 600}}>
<WorkspaceSection config={this.props.config} configSchema={this.props.configSchema} />
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.notifications}
keyName="Notifications"
keyPath="core.notifications"
config={this.props.config} />
<div className="platform-note platform-linux-only">
N1 desktop notifications on Linux require Zenity. You may need to install
it with your package manager (i.e., <code>sudo apt-get install zenity</code>).
</div>
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.reading}
keyName="Reading"
keyPath="core.reading"
config={this.props.config} />
<SendingSection config={this.props.config} configSchema={this.props.configSchema} />
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.attachments}
keyName="Attachments"
keyPath="core.attachments"
config={this.props.config} />
<ConfigSchemaItem
configSchema={props.configSchema.properties.notifications}
keyName="Notifications"
keyPath="core.notifications"
config={props.config}
/>
<div className="platform-note platform-linux-only">
N1 desktop notifications on Linux require Zenity. You may need to install
it with your package manager (i.e., <code>sudo apt-get install zenity</code>).
</div>
);
}
<ConfigSchemaItem
configSchema={props.configSchema.properties.reading}
keyName="Reading"
keyPath="core.reading"
config={props.config}
/>
<SendingSection config={props.config} configSchema={props.configSchema} />
<ConfigSchemaItem
configSchema={props.configSchema.properties.attachments}
keyName="Attachments"
keyPath="core.attachments"
config={props.config}
/>
</div>
);
}
PreferencesGeneral.propTypes = {
config: React.PropTypes.object,
configSchema: React.PropTypes.object,
};
export default PreferencesGeneral;

View file

@ -111,7 +111,8 @@ class PreferencesKeymaps extends React.Component {
style={{margin: 0}}
tabIndex={-1}
value={this.props.config.get('core.keymapTemplate')}
onChange={(event) => this.props.config.set('core.keymapTemplate', event.target.value)}>
onChange={(event) => this.props.config.set('core.keymapTemplate', event.target.value)}
>
{this.state.templates.map((template) => {
return <option key={template} value={template}>{template}</option>
})}

View file

@ -141,7 +141,8 @@ class PreferencesMailRules extends React.Component {
<select
value={this.state.currentAccount.accountId}
onChange={this._onSelectAccount}
style={{margin: 0}} >
style={{margin: 0}}
>
{options}
</select>
);
@ -154,7 +155,8 @@ class PreferencesMailRules extends React.Component {
<RetinaImg
className="icon-mail-rules"
name="rules-big.png"
mode={RetinaImg.Mode.ContentDark} />
mode={RetinaImg.Mode.ContentDark}
/>
<h2>No rules</h2>
<button className="btn btn-small" onMouseDown={this._onAddRule}>
Create a new rule
@ -174,7 +176,8 @@ class PreferencesMailRules extends React.Component {
onDeleteItem={this._onDeleteRule}
onItemEdited={this._onRuleNameEdited}
selected={this.state.selectedRule}
onSelectItem={this._onSelectRule} />
onSelectItem={this._onSelectRule}
/>
{this._renderDetail()}
</Flexbox>
);
@ -204,14 +207,16 @@ class PreferencesMailRules extends React.Component {
<ScenarioEditor
instances={rule.conditions}
templates={this.state.conditionTemplates}
onChange={ (conditions) => Actions.updateMailRule(rule.id, {conditions}) }
className="well well-matchers"/>
onChange={(conditions) => Actions.updateMailRule(rule.id, {conditions})}
className="well well-matchers"
/>
<span>Perform the following actions:</span>
<ScenarioEditor
instances={rule.actions}
templates={this.state.actionTemplates}
onChange={ (actions) => Actions.updateMailRule(rule.id, {actions}) }
className="well well-actions"/>
onChange={(actions) => Actions.updateMailRule(rule.id, {actions})}
className="well well-actions"
/>
</div>
</ScrollRegion>
);
@ -251,7 +256,9 @@ class PreferencesMailRules extends React.Component {
{`${Number(task.numberOfImpactedItems()).toLocaleString()} processed...`}
</div>
<div style={{flex: 1}}></div>
<button className="btn btn-sm" onClick={() => Actions.dequeueTask(task.id) }>Cancel</button>
<button className="btn btn-sm" onClick={() => Actions.dequeueTask(task.id)}>
Cancel
</button>
</Flexbox>
);
})}
@ -277,7 +284,7 @@ class PreferencesMailRules extends React.Component {
<Flexbox style={{marginTop: 40, maxWidth: 600}}>
<div>
<button disabled={processDisabled} className="btn" style={{float: 'right'}} onClick={this._onReprocessRules}>
<button disabled={processDisabled} className="btn" style={{'float': 'right'}} onClick={this._onReprocessRules}>
Process entire inbox
</button>
</div>

View file

@ -16,19 +16,19 @@ class SendingSection extends React.Component {
_getExtendedSchema(configSchema) {
const accounts = AccountStore.accounts();
let values = accounts.map((acc) => {return acc.id});
let labels = accounts.map((acc) => {return acc.me().toString()});
let values = accounts.map(acc => acc.id);
let labels = accounts.map(acc => acc.me().toString());
values = ['selected-mailbox'].concat(values);
labels = ['Account of selected mailbox'].concat(labels);
_.extend(configSchema.properties.sending.properties, {
defaultAccountIdForSend: {
type: 'string',
title: 'Send new messages from',
default: 'selected-mailbox',
enum: values,
enumLabels: labels,
'type': 'string',
'title': 'Send new messages from',
'default': 'selected-mailbox',
'enum': values,
'enumLabels': labels,
},
});
@ -43,7 +43,8 @@ class SendingSection extends React.Component {
config={this.props.config}
configSchema={sendingSchema}
keyName="Sending"
keyPath="core.sending" />
keyPath="core.sending"
/>
);
}

View file

@ -111,49 +111,47 @@ class LaunchSystemStartItem extends React.Component {
}
class WorkspaceSection extends React.Component {
const WorkspaceSection = (props) => {
return (
<section>
<DefaultMailClientItem />
static displayName = 'WorkspaceSection';
<LaunchSystemStartItem />
static propTypes = {
config: React.PropTypes.object,
configSchema: React.PropTypes.object,
}
<ConfigSchemaItem
configSchema={props.configSchema.properties.workspace.properties.systemTray}
keyPath="core.workspace.systemTray"
config={props.config}
/>
render() {
return (
<section>
<DefaultMailClientItem />
<ConfigSchemaItem
configSchema={props.configSchema.properties.workspace.properties.showImportant}
keyPath="core.workspace.showImportant"
config={props.config}
/>
<LaunchSystemStartItem />
<ConfigSchemaItem
configSchema={props.configSchema.properties.workspace.properties.showUnreadForAllCategories}
keyPath="core.workspace.showUnreadForAllCategories"
config={props.config}
/>
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.workspace.properties.systemTray}
keyPath="core.workspace.systemTray"
config={this.props.config} />
<ConfigSchemaItem
configSchema={props.configSchema.properties.workspace.properties.interfaceZoom}
keyPath="core.workspace.interfaceZoom"
config={props.config}
/>
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.workspace.properties.showImportant}
keyPath="core.workspace.showImportant"
config={this.props.config} />
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.workspace.properties.showUnreadForAllCategories}
keyPath="core.workspace.showUnreadForAllCategories"
config={this.props.config} />
<ConfigSchemaItem
configSchema={this.props.configSchema.properties.workspace.properties.interfaceZoom}
keyPath="core.workspace.interfaceZoom"
config={this.props.config} />
<div className="platform-note platform-linux-only">
N1 launch on system start only works in XDG-compliant desktop environments.
</div>
</section>
);
}
<div className="platform-note platform-linux-only">
N1 launch on system start only works in XDG-compliant desktop environments.
</div>
</section>
);
}
WorkspaceSection.propTypes = {
config: React.PropTypes.object,
configSchema: React.PropTypes.object,
}
export default WorkspaceSection;

View file

@ -57,7 +57,8 @@ class ThemePicker extends React.Component {
key={theme.name}
theme={theme}
active={this.state.activeTheme === theme.name}
onSelect={() => this._setActiveTheme(theme.name)} />
onSelect={() => this._setActiveTheme(theme.name)}
/>
);
}
@ -70,21 +71,24 @@ class ThemePicker extends React.Component {
style={{width: "14", WebkitFilter: "none"}}
name="picker-close.png"
mode={RetinaImg.Mode.ContentDark}
onMouseDown={() => Actions.closeModal()} />
onMouseDown={() => Actions.closeModal()}
/>
<h4 style={{color: "#434648"}}>Themes</h4>
<div style={{color: "rgba(35, 31, 32, 0.5)", fontSize: "12px"}}>Click any theme to apply:</div>
<ScrollRegion style={{margin: "10px 5px 0 5px", height: "290px"}}>
<Flexbox
direction="row"
height="auto"
style={{alignItems: "flex-start", flexWrap: "wrap"}}>
style={{alignItems: "flex-start", flexWrap: "wrap"}}
>
{this._renderThemeOptions()}
</Flexbox>
</ScrollRegion>
<div className="create-theme">
<a
href="https://github.com/nylas/N1-theme-starter"
style={{color: "#3187e1"}}>
style={{color: "#3187e1"}}
>
Create a Theme
</a>
</div>

View file

@ -6,8 +6,8 @@ import ThemePackage from '../../../src/theme-package';
import ThemePicker from '../lib/theme-picker';
const {resourcePath} = NylasEnv.getLoadSettings();
const light = new ThemePackage(resourcePath + '/internal_packages/ui-light');
const dark = new ThemePackage(resourcePath + '/internal_packages/ui-dark');
const light = new ThemePackage(`${resourcePath}/internal_packages/ui-light`);
const dark = new ThemePackage(`${resourcePath}/internal_packages/ui-dark`);
describe('ThemePicker', function themePicker() {
beforeEach(() => {

View file

@ -29,7 +29,7 @@ class SearchIndexStore {
this.accountIds = _.pluck(AccountStore.accounts(), 'id')
this.initializeIndex()
.then(() => {
console.log('Thread Search: Index built successfully in ' + ((Date.now() - date) / 1000) + 's')
console.log(`Thread Search: Index built successfully in ${((Date.now() - date) / 1000)}s`)
this.unsubscribers = [
AccountStore.listen(::this.onAccountsChanged),
DatabaseStore.listen(::this.onDataChanged),
@ -50,7 +50,7 @@ class SearchIndexStore {
initializeIndex() {
return DatabaseStore.searchIndexSize(Thread)
.then((size) => {
console.log('Thread Search: Current index size is ' + (size || 0) + ' threads')
console.log(`Thread Search: Current index size is ${(size || 0)} threads`)
if (!size || size >= MAX_INDEX_SIZE || size === 0) {
return this.clearIndex().thenReturn(this.accountIds)
}
@ -85,7 +85,7 @@ class SearchIndexStore {
return;
}
const date = Date.now()
console.log('Thread Search: Updating thread search index for accounts: ' + latestIds)
console.log(`Thread Search: Updating thread search index for accounts ${latestIds}`)
const newIds = _.difference(latestIds, this.accountIds)
const removedIds = _.difference(this.accountIds, latestIds)
@ -102,7 +102,7 @@ class SearchIndexStore {
this.accountIds = latestIds
Promise.all(promises)
.then(() => {
console.log('Thread Search: Index updated successfully in ' + ((Date.now() - date) / 1000) + 's')
console.log(`Thread Search: Index updated successfully in ${((Date.now() - date) / 1000)}s`)
})
})
})
@ -212,9 +212,7 @@ class SearchIndexStore {
{body: QuotedHTMLTransformer.removeQuotedHTML(body)}
))
.map(({body, snippet}) => (
snippet ?
snippet :
Utils.extractTextFromHtml(body, {maxLength: MESSAGE_BODY_LENGTH}).replace(/(\s)+/g, ' ')
snippet || Utils.extractTextFromHtml(body, {maxLength: MESSAGE_BODY_LENGTH}).replace(/(\s)+/g, ' ')
))
.join(' ')
)

View file

@ -88,7 +88,7 @@ class SnoozePopover extends Component {
<div
key={itemLabel}
className="snooze-item"
onClick={this.onSnooze.bind(this, date, itemLabel)}
onClick={() => this.onSnooze(date, itemLabel)}
>
<RetinaImg
url={iconPath}

View file

@ -46,7 +46,7 @@ const SnoozeUtils = {
const task = new SyncbackCategoryTask({category})
Actions.queueTask(task)
return TaskQueueStatusStore.waitForPerformRemote(task).then(()=>{
return TaskQueueStatusStore.waitForPerformRemote(task).then(() => {
return DatabaseStore.findBy(Category, {clientId: category.clientId})
.then((updatedCat) => {
if (updatedCat && updatedCat.isSavedRemotely()) {

View file

@ -17,12 +17,12 @@ describe('SnoozeStore', function snoozeStore() {
this.accounts = [{id: 123}, {id: 321}]
this.snoozeCatsByAccount = {
'123': new Category({accountId: 123, displayName: this.name, serverId: 'sn-1'}),
'321': new Category({accountId: 321, displayName: this.name, serverId: 'sn-2'}),
123: new Category({accountId: 123, displayName: this.name, serverId: 'sn-1'}),
321: new Category({accountId: 321, displayName: this.name, serverId: 'sn-2'}),
}
this.inboxCatsByAccount = {
'123': new Category({accountId: 123, name: 'inbox', serverId: 'in-1'}),
'321': new Category({accountId: 321, name: 'inbox', serverId: 'in-2'}),
123: new Category({accountId: 123, name: 'inbox', serverId: 'in-1'}),
321: new Category({accountId: 321, name: 'inbox', serverId: 'in-2'}),
}
this.threads = [
new Thread({accountId: 123, serverId: 's-1'}),
@ -30,12 +30,12 @@ describe('SnoozeStore', function snoozeStore() {
new Thread({accountId: 321, serverId: 's-3'}),
]
this.updatedThreadsByAccountId = {
'123': {
123: {
threads: [this.threads[0], this.threads[1]],
snoozeCategoryId: 'sn-1',
returnCategoryId: 'in-1',
},
'321': {
321: {
threads: [this.threads[2]],
snoozeCategoryId: 'sn-2',
returnCategoryId: 'in-2',

View file

@ -1,141 +0,0 @@
import * as adapter from '../../src/extensions/composer-extension-adapter';
import {DOMUtils} from 'nylas-exports';
const selection = 'selection';
const node = 'node';
const event = 'event';
const extra = 'extra';
const editor = {
rootNode: node,
currentSelection() {
return selection;
},
};
describe('ComposerExtensionAdapter', function composerExtensionAdapter() {
describe('adaptOnInput', () => {
it('adapts correctly if onContentChanged already defined', () => {
const onInputSpy = jasmine.createSpy('onInput');
const extension = {
onContentChanged() {},
onInput(ev, editableNode, sel) {
onInputSpy(ev, editableNode, sel);
},
};
adapter.adaptOnInput(extension);
extension.onContentChanged({editor, mutations: []});
expect(onInputSpy).not.toHaveBeenCalled();
});
it('adapts correctly when signature is (event, ...)', () => {
const onInputSpy = jasmine.createSpy('onInput');
const extension = {
onInput(ev, editableNode, sel) {
onInputSpy(ev, editableNode, sel);
},
};
adapter.adaptOnInput(extension);
expect(extension.onContentChanged).toBeDefined();
extension.onContentChanged({editor, mutations: []});
expect(onInputSpy).toHaveBeenCalledWith([], node, selection);
});
it('adapts correctly when signature is (editableNode, selection, ...)', () => {
const onInputSpy = jasmine.createSpy('onInput');
const extension = {
onInput(editableNode, sel, ev) {
onInputSpy(editableNode, sel, ev);
},
};
adapter.adaptOnInput(extension);
expect(extension.onContentChanged).toBeDefined();
extension.onContentChanged({editor, mutations: []});
expect(onInputSpy).toHaveBeenCalledWith(node, selection, []);
});
});
describe('adaptOnTabDown', () => {
it('adapts onTabDown correctly', () => {
const onTabDownSpy = jasmine.createSpy('onTabDownSpy');
const mockEvent = {key: 'Tab'};
const range = 'range';
spyOn(DOMUtils, 'getRangeInScope').andReturn(range);
const extension = {
onTabDown(editableNode, rn, ev) {
onTabDownSpy(editableNode, rn, ev);
},
};
adapter.adaptOnTabDown(extension, 'method');
expect(extension.onKeyDown).toBeDefined();
extension.onKeyDown({editor, event: mockEvent});
expect(onTabDownSpy).toHaveBeenCalledWith(node, range, mockEvent);
});
});
describe('adaptContenteditableMethod', () => {
it('adapts correctly when signature is (editor, ...)', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(e, ev, other) {
methodSpy(e, ev, other);
},
};
adapter.adaptContenteditableMethod(extension, 'method');
extension.method({editor, event, extra});
expect(methodSpy).toHaveBeenCalledWith(editor, event, extra);
});
it('adapts correctly when signature is (event, ...)', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(ev, editableNode, sel, other) {
methodSpy(ev, editableNode, sel, other);
},
};
adapter.adaptContenteditableMethod(extension, 'method');
extension.method({editor, event, extra});
expect(methodSpy).toHaveBeenCalledWith(event, node, selection, extra);
});
it('adapts correctly when signature is (editableNode, selection, ...)', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(editableNode, sel, ev, other) {
methodSpy(editableNode, sel, ev, other);
},
};
adapter.adaptContenteditableMethod(extension, 'method');
extension.method({editor, event, extra});
expect(methodSpy).toHaveBeenCalledWith(node, selection, event, extra);
});
it('adapts correctly when using mutations instead of an event', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(e, mutations) {
methodSpy(e, mutations);
},
};
adapter.adaptContenteditableMethod(extension, 'method');
extension.method({editor, mutations: []});
expect(methodSpy).toHaveBeenCalledWith(editor, []);
});
});
describe('adaptComposerMethod', () => {
it('adapts correctly for other composer extension methods', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const draft = 'draft';
const extension = {
warningsForSending(dr) {
methodSpy(dr);
return 'result';
},
};
adapter.adaptComposerMethod(extension, 'warningsForSending');
const res = extension.warningsForSending({draft});
expect(res).toEqual('result');
expect(methodSpy).toHaveBeenCalledWith(draft);
});
});
});

View file

@ -40,31 +40,31 @@ import ModelWithMetadata from './model-with-metadata'
class Thread extends ModelWithMetadata {
static attributes = _.extend({}, ModelWithMetadata.attributes, {
'snippet': Attributes.String({
snippet: Attributes.String({
modelKey: 'snippet',
}),
'subject': Attributes.String({
subject: Attributes.String({
queryable: true,
modelKey: 'subject',
}),
'unread': Attributes.Boolean({
unread: Attributes.Boolean({
queryable: true,
modelKey: 'unread',
}),
'starred': Attributes.Boolean({
starred: Attributes.Boolean({
queryable: true,
modelKey: 'starred',
}),
'version': Attributes.Number({
version: Attributes.Number({
queryable: true,
modelKey: 'version',
}),
'categories': Attributes.Collection({
categories: Attributes.Collection({
queryable: true,
modelKey: 'categories',
joinOnField: 'id',
@ -72,11 +72,11 @@ class Thread extends ModelWithMetadata {
itemClass: Category,
}),
'categoriesType': Attributes.String({
categoriesType: Attributes.String({
modelKey: 'categoriesType',
}),
'participants': Attributes.Collection({
participants: Attributes.Collection({
queryable: true,
modelKey: 'participants',
joinOnField: 'email',
@ -84,23 +84,23 @@ class Thread extends ModelWithMetadata {
itemClass: Contact,
}),
'hasAttachments': Attributes.Boolean({
hasAttachments: Attributes.Boolean({
modelKey: 'has_attachments',
}),
'lastMessageReceivedTimestamp': Attributes.DateTime({
lastMessageReceivedTimestamp: Attributes.DateTime({
queryable: true,
modelKey: 'lastMessageReceivedTimestamp',
jsonKey: 'last_message_received_timestamp',
}),
'lastMessageSentTimestamp': Attributes.DateTime({
lastMessageSentTimestamp: Attributes.DateTime({
queryable: true,
modelKey: 'lastMessageSentTimestamp',
jsonKey: 'last_message_sent_timestamp',
}),
'inAllMail': Attributes.Boolean({
inAllMail: Attributes.Boolean({
queryable: true,
modelKey: 'inAllMail',
jsonKey: 'in_all_mail',

@ -1 +1 @@
Subproject commit 797179d156bfeaba611e755b8c8abdc4615df0c8
Subproject commit d2ba2ad629341375009b6270c3906c5933e1a255