/* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ import { Message } from '../../src/flux/models/message'; import * as Actions from '../../src/flux/actions'; import DatabaseStore from '../../src/flux/stores/database-store'; import { DraftEditingSession } from '../../src/flux/stores/draft-editing-session'; const { DraftChangeSet } = DraftEditingSession; import _ from 'underscore'; xdescribe('DraftEditingSession Specs', function() { describe('DraftChangeSet', function() { beforeEach(function() { this.onDidAddChanges = jasmine.createSpy('onDidAddChanges'); this.onWillAddChanges = jasmine.createSpy('onWillAddChanges'); this.commitResolve = null; this.commitResolves = []; this.onCommit = jasmine.createSpy('commit').andCallFake(() => { new Promise((resolve, reject) => { this.commitResolves.push(resolve); this.commitResolve = resolve; }); }); this.changeSet = new DraftChangeSet({ onDidAddChanges: this.onDidAddChanges, onWillAddChanges: this.onWillAddChanges, onCommit: this.onCommit, }); this.changeSet._pending = { subject: 'Change to subject line' }; }); describe('teardown', () => it('should remove all of the pending and saving changes', function() { this.changeSet.teardown(); expect(this.changeSet._saving).toEqual({}); expect(this.changeSet._pending).toEqual({}); })); describe('add', function() { it('should mark that the draft is not pristine', function() { this.changeSet.add({ body: 'Hello World!' }); expect(this.changeSet._pending.pristine).toEqual(false); }); it('should add the changes to the _pending set', function() { this.changeSet.add({ body: 'Hello World!' }); expect(this.changeSet._pending.body).toEqual('Hello World!'); }); describe('otherwise', () => it('should commit after thirty seconds', function() { spyOn(this.changeSet, 'commit'); this.changeSet.add({ body: 'Hello World!' }); expect(this.changeSet.commit).not.toHaveBeenCalled(); advanceClock(31000); expect(this.changeSet.commit).toHaveBeenCalled(); })); }); describe('commit', function() { it('should resolve immediately if the pending set is empty', function() { this.changeSet._pending = {}; waitsForPromise(() => { return this.changeSet.commit().then(() => { expect(this.onCommit).not.toHaveBeenCalled(); }); }); }); it('should move changes to the saving set', function() { const pendingBefore = { ...this.changeSet._pending }; expect(this.changeSet._saving).toEqual({}); this.changeSet.commit(); advanceClock(); expect(this.changeSet._pending).toEqual({}); expect(this.changeSet._saving).toEqual(pendingBefore); }); it('should call the commit handler and then clear the saving set', function() { this.changeSet.commit(); advanceClock(); expect(this.changeSet._saving).not.toEqual({}); this.commitResolve(); advanceClock(); expect(this.changeSet._saving).toEqual({}); }); describe('concurrency', () => it('the commit function should always run serially', function() { let firstFulfilled = false; let secondFulfilled = false; this.changeSet._pending = { subject: 'A' }; this.changeSet.commit().then(() => { this.changeSet._pending = { subject: 'B' }; firstFulfilled = true; }); this.changeSet.commit().then(() => { secondFulfilled = true; }); advanceClock(); expect(firstFulfilled).toBe(false); expect(secondFulfilled).toBe(false); this.commitResolves[0](); advanceClock(); expect(firstFulfilled).toBe(true); expect(secondFulfilled).toBe(false); this.commitResolves[1](); advanceClock(); expect(firstFulfilled).toBe(true); expect(secondFulfilled).toBe(true); })); }); }); describe('DraftEditingSession', function() { describe('constructor', function() { it('should make a query to fetch the draft', function() { spyOn(DatabaseStore, 'run').andCallFake(() => { return new Promise((resolve, reject) => {}); }); const session = new DraftEditingSession('client-id'); expect(DatabaseStore.run).toHaveBeenCalled(); }); describe('when given a draft object', function() { beforeEach(function() { spyOn(DatabaseStore, 'run'); this.draft = new Message({ draft: true, body: '123' }); this.session = new DraftEditingSession('client-id', this.draft); }); it('should not make a query for the draft', () => expect(DatabaseStore.run).not.toHaveBeenCalled()); it('prepare should resolve without querying for the draft', function() { waitsForPromise(() => this.session.prepare().then(() => { expect(this.session.draft()).toBeDefined(); expect(DatabaseStore.run).not.toHaveBeenCalled(); }) ); }); }); }); describe('prepare', function() { beforeEach(function() { this.draft = new Message({ draft: true, body: '123', id: 'client-id' }); spyOn(DraftEditingSession.prototype, 'prepare'); this.session = new DraftEditingSession('client-id'); spyOn(this.session, '_setDraft').andCallThrough(); spyOn(DatabaseStore, 'run').andCallFake(modelQuery => { return Promise.resolve(this.draft); }); jasmine.unspy(DraftEditingSession.prototype, 'prepare'); }); it('should call setDraft with the retrieved draft', function() { waitsForPromise(() => { return this.session.prepare().then(() => { expect(this.session._setDraft).toHaveBeenCalledWith(this.draft); }); }); }); it('should resolve with the DraftEditingSession', function() { waitsForPromise(() => { return this.session.prepare().then(val => { expect(val).toBe(this.session); }); }); }); describe('error handling', function() { it('should reject if the draft session has already been destroyed', function() { waitsForPromise(() => { return this.session .prepare() .then(() => { expect(false).toBe(true); }) .catch(val => { expect(val instanceof Error).toBe(true); }); }); }); it('should reject if the draft cannot be found', function() { this.draft = null; waitsForPromise(() => { return this.session .prepare() .then(() => { expect(false).toBe(true); }) .catch(val => { expect(val instanceof Error).toBe(true); }); }); }); }); }); describe('when a draft changes', function() { beforeEach(function() { this.draft = new Message({ draft: true, id: 'client-id', body: 'A', subject: 'initial' }); this.session = new DraftEditingSession('client-id', this.draft); advanceClock(); spyOn(Actions, 'queueTask').andReturn(Promise.resolve()); }); it('should ignore the update unless it applies to the current draft', function() { spyOn(this.session, 'trigger'); this.session._onDraftChanged({ objectClass: 'message', objects: [new Message()] }); expect(this.session.trigger).not.toHaveBeenCalled(); this.session._onDraftChanged({ objectClass: 'message', objects: [this.draft] }); expect(this.session.trigger).toHaveBeenCalled(); }); it('should apply the update to the current draft', function() { const updatedDraft = this.draft.clone(); updatedDraft.subject = 'This is the new subject'; spyOn(this.session, '_setDraft'); this.session._onDraftChanged({ objectClass: 'message', objects: [updatedDraft] }); expect(this.session._setDraft).toHaveBeenCalled(); const draft = this.session._setDraft.calls[0].args[0]; expect(draft.subject).toEqual(updatedDraft.subject); }); it('atomically commits changes', function() { spyOn(DatabaseStore, 'run').andReturn(Promise.resolve(this.draft)); spyOn(DatabaseStore, 'inTransaction').andCallThrough(); this.session.changes.add({ body: '123' }); waitsForPromise(() => { return this.session.changes.commit().then(() => { expect(DatabaseStore.inTransaction).toHaveBeenCalled(); expect(DatabaseStore.inTransaction.calls.length).toBe(1); }); }); }); it('persist the applied changes', function() { spyOn(DatabaseStore, 'run').andReturn(Promise.resolve(this.draft)); this.session.changes.add({ body: '123' }); waitsForPromise(() => { return this.session.changes.commit().then(() => { expect(updated.body).toBe('123'); }); }); }); describe('when findBy does not return a draft', () => it("continues and persists it's local draft reference, so it is resaved and draft editing can continue", function() { spyOn(DatabaseStore, 'run').andReturn(Promise.resolve(null)); this.session.changes.add({ body: '123' }); waitsForPromise(() => { return this.session.changes.commit().then(() => { expect(updated.body).toBe('123'); }); }); })); it('does nothing if the draft is marked as destroyed', function() { spyOn(DatabaseStore, 'run').andReturn(Promise.resolve(this.draft)); spyOn(DatabaseStore, 'inTransaction').andCallThrough(); return waitsForPromise(() => { this.session.changes.add({ body: '123' }); return this.session.changes.commit().then(() => { expect(DatabaseStore.inTransaction).not.toHaveBeenCalled(); }); }); }); }); }); });