Merge pull request #1784 from garyhgohoos/23593-2
[xtuple] / test / extensions / all / workspace.js
1 /*jshint trailing:true, white:true, indent:2, strict:true, curly:true,
2   immed:true, eqeqeq:true, forin:true, latedef:true,
3   newcap:true, noarg:true, undef:true */
4 /*global XT:true, XM:true, XV:true, process:true, module:true, require:true,
5   describe:true, before:true, enyo:true, it:true, _:true, console:true,
6   beforeEach:true, afterEach:true, setTimeout:true, setInterval:true,
7   clearInterval:true*/
8
9 (function () {
10   "use strict";
11
12   var zombieAuth = require("../../lib/zombie_auth"),
13     _ = require("underscore"),
14     smoke = require("../../lib/smoke"),
15     common = require("../../lib/common"),
16     assert = require("chai").assert;
17
18   describe('Workspaces', function () {
19     this.timeout(60 * 1000);
20     it('should log in first', function (done) {
21       zombieAuth.loadApp(done);
22     });
23
24     it('should be plumbed correctly', function () {
25       // look at all the workspaces in XV
26       _.each(XV, function (value, key) {
27         if (XV.inheritsFrom(value.prototype, "XV.Workspace")) {
28           // XXX TODO WorkOrderWorkspace should not be here
29           if (_.contains(['SalesOrderBase', 'AccountDocumentWorkspace', 'OrderedReferenceWorkspace', 'EmailProfileWorkspace', 'WorkOrderWorkspace'], key) ||
30               value.prototype.modelAmnesty) {
31             // exclude abstract classes and child workspaces
32             return;
33           }
34
35           describe('XV.' + key, function () {
36             it('should reflect well in the history panel', function () {
37               var master = new enyo.Control(),
38                 Klass,
39                 workspace = master.createComponent({kind: "XV." + key});
40
41               if (workspace.model) {
42                 Klass = XT.getObjectByName(workspace.model);
43                 assert.isNotNull(Klass);
44                 if (Klass.prototype.getAttributeNames().indexOf(Klass.prototype.nameAttribute) < 0 &&
45                     typeof Klass.prototype[Klass.prototype.nameAttribute] !== 'function' &&
46                     Klass.prototype.nameAttribute.indexOf(".") < 0 && // don't bother with dotted nameAttributes
47                     Klass.prototype.keepInHistory &&
48                     Klass.prototype.idAttribute === 'uuid') {
49                   assert.fail(0, 1, workspace.model + " does not contain its nameAttribute, which will reflect " +
50                     "poorly in the history panel");
51                 }
52               }
53             });
54
55             it('should have its attrs set up right', function () {
56               var master = new enyo.Control(),
57                 workspace = master.createComponent({kind: "XV." + key}),
58                 Klass = XT.getObjectByName(workspace.getModel()),
59                 model = new Klass();
60
61               if (model.meta) {
62                 // workspaces with models with meta might mislead us
63                 return;
64               }
65               var attrs = _.compact(_.map(workspace.$, function (component) {
66                 return component.attr;
67               }));
68               _.each(attrs, function (attr) {
69                 common.verifyAttr(attr, workspace.model);
70               });
71             });
72           });
73         }
74       });
75     });
76   });
77
78   /**
79     * Test the INCDT-21110 fix.
80     * http://www.xtuple.org/xtincident/view/default/21110
81     */
82   describe.skip('INCDT-21110: Record remains locked when Back->Discard selected', function () {
83     var workspaceContainer, workspace, model, id, moduleContainer;
84
85     beforeEach(function (done) {
86       this.timeout(60 * 1000);
87
88       smoke.navigateToExistingWorkspace(XT.app, "XV.ClassCodeList", function (_workspaceContainer) {
89         workspaceContainer = _workspaceContainer;
90         moduleContainer = XT.app.$.postbooks;
91         assert.equal(workspaceContainer, XT.app.$.postbooks.getActive());
92
93         workspace = workspaceContainer.$.workspace;
94         id = workspace.getValue().id;
95         model = workspace.getValue();
96
97         assert.isTrue(model.hasLockKey(), "model should have lock");
98         assert.isFalse(model.isNew());
99
100         done();
101       });
102     });
103     afterEach(function (done) {
104       this.timeout(60 * 1000);
105
106       // maybe one of the tests already released the lock
107       if (!model.hasLockKey()) {
108         done();
109         return;
110       }
111       model.on("lockChange", function () {
112         model.off("lockChange");
113         assert.isFalse(model.hasLockKey());
114         // XXX solves inexplicable race condition
115         setTimeout(function () {
116           done();
117         }, 4000);
118       });
119       workspaceContainer.close();
120     });
121     it('test base case', function () {
122
123     });
124     it('test lock condition on "close and discard"', function (done) {
125         /**
126           * This test presupposes that we have obtained the lock. When the
127           * lock has been released, the test completes.
128           */
129       var handleLockChange = function () {
130           model.off("lockChange", handleLockChange);
131           assert.isFalse(model.hasLockKey());
132           done();
133         },
134         /**
135           * Guard on notifyPopup; when the notifyPopup is showing, 'tap' the
136           * popup's "Discard" button.
137           */
138         notifyPopupInterval = setInterval(function () {
139           if (!moduleContainer.$.notifyPopup.showing) { return; }
140
141           clearInterval(notifyPopupInterval);
142           model.on("lockChange", handleLockChange);
143           moduleContainer.notifyTap(null, { originator: { name: "notifyNo" }});
144         }, 100),
145         /**
146           * When the model is READY_CLEAN, edit the value of inputWidget
147           */
148         handleBeforeEdit = function () {
149           if (model.isDirty()) { return; }
150
151           model.off("statusChange", handleBeforeEdit);
152           workspace.$.inputWidget.setValue("a valid name");
153         },
154         /**
155           * When the model becomes dirty as a result of the edit, 'tap' the
156           * container's "Back" button.
157           */
158         handleAfterEdit = function () {
159           if (!model.isDirty()) { return; }
160
161           model.off("statusChange", handleAfterEdit);
162           workspaceContainer.$.backButton.bubble("onclick");
163         };
164
165       model.on("statusChange", handleBeforeEdit);
166       model.on("statusChange", handleAfterEdit);
167     });
168   });
169 }());