Преглед на файлове

HUE-9090 [frontend] Add tests for the event-based modal components

Johan Ahlen преди 6 години
родител
ревизия
60d3c9477e

+ 21 - 0
desktop/core/src/desktop/js/ko/components/__snapshots__/ko.createDirectoryModal.test.js.snap

@@ -0,0 +1,21 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.createDirectoryModal.js should render component 1`] = `
+"<html><head></head><body><div id=\\"createDirectoryModal\\" data-bind=\\"descendantsComplete: descendantsComplete, component: { name: 'create-directory', params: params }\\" data-keyboard=\\"true\\" class=\\"modal hide fade\\" tabindex=\\"-1\\">
+  <!-- ko with: activeEntry -->
+  <form class=\\"form-horizontal\\">
+    <div class=\\"modal-header\\">
+      <button type=\\"button\\" class=\\"close\\" data-dismiss=\\"modal\\" aria-label=\\"Close\\"><span aria-hidden=\\"true\\">×</span></button>
+      <h2 class=\\"modal-title\\">Create Directory</h2>
+    </div>
+    <div class=\\"modal-body \\">
+      <input id=\\"newDirectoryName\\" class=\\"input large-as-modal\\" type=\\"text\\" placeholder=\\"Directory name\\">
+    </div>
+    <div class=\\"modal-footer\\">
+      <input type=\\"button\\" class=\\"btn\\" data-dismiss=\\"modal\\" data-bind=\\"click: function () { $('#newDirectoryName').val(null) }\\" value=\\"Cancel\\">
+      <input type=\\"submit\\" class=\\"btn btn-primary disable-feedback\\" value=\\"Create\\" data-bind=\\"click: function () { if ($('#newDirectoryName').val()) { $data.createDirectory($('#newDirectoryName').val()); $('#createDirectoryModal').modal('hide'); } }\\">
+    </div>
+  </form>
+  <!-- /ko -->
+</div></body></html>"
+`;

+ 27 - 0
desktop/core/src/desktop/js/ko/components/__snapshots__/ko.deleteDocModal.test.js.snap

@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.deleteDocModal.js should render component 1`] = `
+"<html><head></head><body><div id=\\"deleteEntriesModal\\" data-bind=\\"descendantsComplete: descendantsComplete, component: { name: 'delete-entry', params: params }\\" data-keyboard=\\"true\\" class=\\"modal hide fade\\" tabindex=\\"-1\\">
+  <!-- ko with: activeEntry -->
+  <div class=\\"modal-header\\">
+    <button type=\\"button\\" class=\\"close\\" data-dismiss=\\"modal\\" aria-label=\\"Close\\"><span aria-hidden=\\"true\\">×</span></button>
+    <!-- ko if: entriesToDelete().length === 0 -->
+    <h2 class=\\"modal-title\\">The trash is empty</h2>
+    <!-- /ko -->
+    <!-- ko if: entriesToDelete().length > 0 --><!-- /ko -->
+  </div>
+  <div class=\\"modal-body\\">
+    <div class=\\"doc-browser-empty animated\\" style=\\"display: none;\\" data-bind=\\"visible: entriesToDelete().length > 0 &amp;&amp; selectedDocsWithDependents().length === 0\\">
+      <i class=\\"fa fa-spinner fa-spin fa-2x\\"></i>
+    </div>
+    <ul data-bind=\\"foreach: selectedDocsWithDependents()\\"></ul>
+  </div>
+  <div class=\\"modal-footer\\">
+    <!-- ko if: entriesToDelete().length === 0 -->
+    <input type=\\"button\\" class=\\"btn\\" data-dismiss=\\"modal\\" value=\\"Close\\">
+    <!-- /ko -->
+    <!-- ko if: entriesToDelete().length > 0 --><!-- /ko -->
+  </div>
+  <!-- /ko -->
+</div></body></html>"
+`;

+ 47 - 0
desktop/core/src/desktop/js/ko/components/__snapshots__/ko.importDocumentsModal.test.js.snap

@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.importDocumentsModal.js should render component 1`] = `
+"<html><head></head><body class=\\"modal-open\\"><div id=\\"importDocumentsModal\\" data-bind=\\"descendantsComplete: descendantsComplete, component: { name: 'import-documents-modal', params: params }\\" data-keyboard=\\"true\\" class=\\"modal hide fade fileupload-modal in\\" tabindex=\\"-1\\" aria-hidden=\\"false\\">
+<div class=\\"modal-header\\">
+  <button type=\\"button\\" class=\\"close\\" data-dismiss=\\"modal\\" aria-label=\\"Close\\"><span aria-hidden=\\"true\\">×</span></button>
+  <!-- ko if: isHistory --><!-- /ko -->
+  <!-- ko ifnot: isHistory -->
+  <h2 class=\\"modal-title\\">Import Hue Documents</h2>
+  <!-- /ko -->
+</div>
+
+<!-- ko ifnot: imported --> 
+<div class=\\"modal-body\\">
+  <!-- ko with: activeEntry -->
+  <form id=\\"importDocumentsForm\\" class=\\"form-horizontal\\" style=\\"display: inline\\" enctype=\\"multipart/form-data\\">
+    <div data-bind=\\"visible: uploading() || uploadComplete()\\" style=\\"display: none;\\">
+      <span data-bind=\\"visible: uploading()\\" style=\\"display: none;\\">Importing...</span>
+      <span data-bind=\\"visible: !uploadFailed() &amp;&amp; uploadComplete()\\" style=\\"display: none;\\">Import complete!</span>
+      <span data-bind=\\"visible: uploadFailed\\" style=\\"display: none;\\">Import failed!</span>
+      <progress data-bind=\\"visible: uploading() || uploadComplete()\\" id=\\"importDocumentsProgress\\" value=\\"0\\" max=\\"100\\" style=\\"width: 560px; display: none;\\"></progress>
+    </div>
+    <div class=\\"pull-right\\">
+      <!-- ko ifnot: uploading() || uploadComplete() -->
+      <input type=\\"button\\" class=\\"btn\\" data-clear=\\"fileupload\\" data-bind=\\"click: closeUploadModal\\" value=\\"Cancel\\">
+      <input type=\\"submit\\" class=\\"btn btn-danger\\" data-clear=\\"fileupload\\" data-bind=\\"enable: importEnabled, click: function() { upload($parent.importComplete) }\\" value=\\"Import\\" disabled=\\"\\">
+      <!-- /ko -->
+      <!-- ko if: uploading() || uploadComplete() --><!-- /ko -->
+    </div>
+
+    <div class=\\"fileupload fileupload-new\\" data-provides=\\"fileupload\\" data-bind=\\"visible: !uploading() &amp;&amp; !uploadComplete()\\">
+      <span class=\\"btn btn-file\\">
+        <span class=\\"fileupload-new\\">Select json file</span>
+        <span class=\\"fileupload-exists\\">Change</span>
+        <input id=\\"importDocumentInput\\" type=\\"file\\" name=\\"documents\\" accept=\\".json\\" data-bind=\\"value: selectedImportFile\\">
+      </span>
+      &nbsp;&nbsp;<span class=\\"fileupload-preview\\"></span>
+      <a href=\\"#\\" class=\\"fileupload-exists\\" data-clear=\\"fileupload\\"><i class=\\"fa fa-times\\"></i></a>
+    </div>
+    <input type=\\"hidden\\" name=\\"path\\" data-bind=\\"value: definition().path\\" value=\\"\\">
+  </form>
+  <!-- /ko -->
+</div>
+<!-- /ko -->
+<!-- ko if: imported --><!-- /ko -->
+</div><div class=\\"modal-backdrop fade in\\"></div></body></html>"
+`;

+ 37 - 0
desktop/core/src/desktop/js/ko/components/__snapshots__/ko.sessionAuthModal.test.js.snap

@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.sessionAuthModal.js should render component 1`] = `
+"<html><head></head><body class=\\"modal-open\\"><div id=\\"sessionAuthModal\\" data-bind=\\"descendantsComplete: descendantsComplete, component: { name: 'session-auth-modal', params: params }\\" data-keyboard=\\"true\\" class=\\"modal hide fade in\\" tabindex=\\"-1\\" aria-hidden=\\"false\\">
+<div class=\\"modal-header\\">
+  <button type=\\"button\\" class=\\"close\\" data-dismiss=\\"modal\\" aria-label=\\"Close\\"><span aria-hidden=\\"true\\">×</span></button>
+  <h2 class=\\"modal-title\\">Connect to the data source</h2>
+</div>
+<div class=\\"modal-body\\">
+  <!-- ko if: authSessionMessage -->
+    <div class=\\"row-fluid\\">
+      <div class=\\"alert-warning\\">
+        <span data-bind=\\"text: authSessionMessage\\">hello</span>
+      </div>
+    </div>
+  <!-- /ko -->
+  <div class=\\"row-fluid\\">
+    <div class=\\"span6\\">
+      <div class=\\"input-prepend\\">
+        <span class=\\"add-on muted\\"><i class=\\"fa fa-user\\"></i></span>
+        <input name=\\"username\\" type=\\"text\\" data-bind=\\"value: $root.authSessionUsername\\" placeholder=\\"Username\\">
+      </div>
+    </div>
+    <div class=\\"span6\\">
+      <div class=\\"input-prepend\\">
+        <span class=\\"add-on muted\\"><i class=\\"fa fa-lock\\"></i></span>
+        <input name=\\"password\\" type=\\"password\\" data-bind=\\"value: $root.authSessionPassword\\" placeholder=\\"Password\\">
+      </div>
+    </div>
+  </div>
+</div>
+<div class=\\"modal-footer\\">
+  <a class=\\"btn\\" data-dismiss=\\"modal\\">Cancel</a>
+  <a class=\\"btn btn-primary disable-feedback\\" data-bind=\\"click: connect, css: { 'disabled': loading }\\"><i class=\\"fa fa-spinner fa-spin\\" data-bind=\\"visible: loading\\" style=\\"display: none;\\"></i> Connect</a>
+</div>
+</div><div class=\\"modal-backdrop fade in\\"></div></body></html>"
+`;

+ 7 - 0
desktop/core/src/desktop/js/ko/components/__snapshots__/ko.shareDocModal.test.js.snap

@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.shareDocModal.js should render component 1`] = `
+"<html><head></head><body class=\\"modal-open\\"><div id=\\"shareDocModal\\" data-bind=\\"descendantsComplete: descendantsComplete, component: { name: 'share-doc-modal', params: params }\\" data-keyboard=\\"true\\" class=\\"modal hide fade in\\" tabindex=\\"-1\\" aria-hidden=\\"false\\">
+  <!-- ko with: document --><!-- /ko -->
+</div><div class=\\"modal-backdrop fade in\\"></div></body></html>"
+`;

+ 13 - 3
desktop/core/src/desktop/js/ko/components/ko.createDirectoryModal.js

@@ -21,6 +21,9 @@ import componentUtils from './componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 
+export const SHOW_EVENT = 'show.create.directory.modal';
+export const SHOWN_EVENT = 'create.directory.modal.shown';
+
 const TEMPLATE = `
   <!-- ko with: activeEntry -->
   <form class="form-horizontal">
@@ -48,18 +51,25 @@ const TEMPLATE = `
 `;
 
 componentUtils.registerComponent('create-directory', undefined, TEMPLATE).then(() => {
-  huePubSub.subscribe('show.create.directory.modal', docViewModel => {
+  huePubSub.subscribe(SHOW_EVENT, docViewModel => {
     let $createDirectoryModal = $('#createDirectoryModal');
     if ($createDirectoryModal.length > 0) {
       ko.cleanNode($createDirectoryModal[0]);
       $createDirectoryModal.remove();
     }
 
+    const data = {
+      params: docViewModel,
+      descendantsComplete: () => {
+        huePubSub.publish(SHOWN_EVENT);
+      }
+    };
+
     $createDirectoryModal = $(
-      '<div id="createDirectoryModal" data-bind="component: { name: \'create-directory\', params: $data }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
+      '<div id="createDirectoryModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'create-directory\', params: params }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
     );
     $('body').append($createDirectoryModal);
 
-    ko.applyBindings(docViewModel, $createDirectoryModal[0]);
+    ko.applyBindings(data, $createDirectoryModal[0]);
   });
 });

+ 31 - 0
desktop/core/src/desktop/js/ko/components/ko.createDirectoryModal.test.js

@@ -0,0 +1,31 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import { SHOWN_EVENT, SHOW_EVENT } from './ko.createDirectoryModal';
+import huePubSub from 'utils/huePubSub';
+
+describe('ko.createDirectoryModal.js', () => {
+  it('should render component', async () => {
+    huePubSub.publish(SHOW_EVENT, { activeEntry: ko.observable({}) });
+
+    await new Promise(resolve => {
+      huePubSub.subscribeOnce(SHOWN_EVENT, resolve);
+    });
+
+    expect(window.document.documentElement.outerHTML).toMatchSnapshot();
+  });
+});

+ 13 - 3
desktop/core/src/desktop/js/ko/components/ko.deleteDocModal.js

@@ -21,6 +21,9 @@ import componentUtils from './componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 
+export const SHOW_EVENT = 'doc.show.delete.modal';
+export const SHOWN_EVENT = 'doc.delete.modal.shown';
+
 const TEMPLATE = `
   <!-- ko with: activeEntry -->
   <div class="modal-header">
@@ -76,18 +79,25 @@ const TEMPLATE = `
 `;
 
 componentUtils.registerComponent('delete-entry', undefined, TEMPLATE).then(() => {
-  huePubSub.subscribe('doc.show.delete.modal', docViewModel => {
+  huePubSub.subscribe(SHOW_EVENT, docViewModel => {
     let $deleteEntriesModal = $('#deleteEntriesModal');
     if ($deleteEntriesModal.length > 0) {
       ko.cleanNode($deleteEntriesModal[0]);
       $deleteEntriesModal.remove();
     }
 
+    const data = {
+      params: docViewModel,
+      descendantsComplete: () => {
+        huePubSub.publish(SHOWN_EVENT);
+      }
+    };
+
     $deleteEntriesModal = $(
-      '<div id="deleteEntriesModal" data-bind="component: { name: \'delete-entry\', params: $data }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
+      '<div id="deleteEntriesModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'delete-entry\', params: params }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
     );
     $('body').append($deleteEntriesModal);
 
-    ko.applyBindings(docViewModel, $deleteEntriesModal[0]);
+    ko.applyBindings(data, $deleteEntriesModal[0]);
   });
 });

+ 36 - 0
desktop/core/src/desktop/js/ko/components/ko.deleteDocModal.test.js

@@ -0,0 +1,36 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import { SHOWN_EVENT, SHOW_EVENT } from './ko.deleteDocModal';
+import huePubSub from 'utils/huePubSub';
+
+describe('ko.deleteDocModal.js', () => {
+  it('should render component', async () => {
+    huePubSub.publish(SHOW_EVENT, {
+      activeEntry: ko.observable({
+        entriesToDelete: ko.observableArray(),
+        selectedDocsWithDependents: ko.observableArray()
+      })
+    });
+
+    await new Promise(resolve => {
+      huePubSub.subscribeOnce(SHOWN_EVENT, resolve);
+    });
+
+    expect(window.document.documentElement.outerHTML).toMatchSnapshot();
+  });
+});

+ 15 - 4
desktop/core/src/desktop/js/ko/components/ko.importDocumentsModal.js

@@ -22,6 +22,10 @@ import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import HueFileEntry from '../../doc/hueFileEntry';
 
+export const HIDE_EVENT = 'hide.import.documents.modal';
+export const SHOW_EVENT = 'show.import.documents.modal';
+export const SHOWN_EVENT = 'import.documents.modal.shown';
+
 const TEMPLATE = `
 <div class="modal-header">
   <button type="button" class="close" data-dismiss="modal" aria-label="${I18n(
@@ -160,15 +164,22 @@ class ImportDocumentsModal {
 }
 
 componentUtils.registerComponent('import-documents-modal', undefined, TEMPLATE).then(() => {
-  huePubSub.subscribe('show.import.documents.modal', params => {
-    huePubSub.publish('hide.import.documents.modal');
+  huePubSub.subscribe(SHOW_EVENT, params => {
+    huePubSub.publish(HIDE_EVENT);
 
     const $importDocumentsModal = $(
-      '<div id="importDocumentsModal" data-bind="component: { name: \'import-documents-modal\', params: $data }" data-keyboard="true" class="modal hide fade fileupload-modal" tabindex="-1"/>'
+      '<div id="importDocumentsModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'import-documents-modal\', params: params }" data-keyboard="true" class="modal hide fade fileupload-modal" tabindex="-1"/>'
     );
     $('body').append($importDocumentsModal);
 
-    ko.applyBindings(new ImportDocumentsModal(params), $importDocumentsModal[0]);
+    const data = {
+      params: new ImportDocumentsModal(params),
+      descendantsComplete: () => {
+        huePubSub.publish(SHOWN_EVENT);
+      }
+    };
+
+    ko.applyBindings(data, $importDocumentsModal[0]);
     $importDocumentsModal.modal('show');
   });
 

+ 38 - 0
desktop/core/src/desktop/js/ko/components/ko.importDocumentsModal.test.js

@@ -0,0 +1,38 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import { SHOWN_EVENT, SHOW_EVENT } from './ko.importDocumentsModal';
+import huePubSub from 'utils/huePubSub';
+
+import 'ext/bootstrap.2.3.2.min';
+
+describe('ko.importDocumentsModal.js', () => {
+  it('should render component', async () => {
+    huePubSub.publish(SHOW_EVENT, {
+      activeEntry: ko.observable({
+        entriesToDelete: ko.observableArray(),
+        selectedDocsWithDependents: ko.observableArray()
+      })
+    });
+
+    await new Promise(resolve => {
+      huePubSub.subscribeOnce(SHOWN_EVENT, resolve);
+    });
+
+    expect(window.document.documentElement.outerHTML).toMatchSnapshot();
+  });
+});

+ 13 - 3
desktop/core/src/desktop/js/ko/components/ko.sessionAuthModal.js

@@ -22,6 +22,9 @@ import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
 
+export const SHOW_EVENT = 'show.session.auth.modal';
+export const SHOWN_EVENT = 'session.auth.modal.shown';
+
 const TEMPLATE = `
 <div class="modal-header">
   <button type="button" class="close" data-dismiss="modal" aria-label="${I18n(
@@ -113,7 +116,7 @@ class SessionAuthModal {
 }
 
 componentUtils.registerComponent('session-auth-modal', undefined, TEMPLATE).then(() => {
-  huePubSub.subscribe('show.session.auth.modal', sessionAuthParams => {
+  huePubSub.subscribe(SHOW_EVENT, sessionAuthParams => {
     let $sessionAuthModal = $('#sessionAuthModal');
     if ($sessionAuthModal.length > 0) {
       ko.cleanNode($sessionAuthModal[0]);
@@ -121,12 +124,19 @@ componentUtils.registerComponent('session-auth-modal', undefined, TEMPLATE).then
     }
 
     $sessionAuthModal = $(
-      '<div id="sessionAuthModal" data-bind="component: { name: \'session-auth-modal\', params: $data }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
+      '<div id="sessionAuthModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'session-auth-modal\', params: params }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
     );
     $('body').append($sessionAuthModal);
 
     const model = new SessionAuthModal(sessionAuthParams, $sessionAuthModal);
-    ko.applyBindings(model, $sessionAuthModal[0]);
+    const data = {
+      params: model,
+      descendantsComplete: () => {
+        huePubSub.publish(SHOWN_EVENT);
+      }
+    };
+
+    ko.applyBindings(data, $sessionAuthModal[0]);
     $sessionAuthModal.modal('show');
     $sessionAuthModal.on('hidden', () => {
       if (model.pending) {

+ 38 - 0
desktop/core/src/desktop/js/ko/components/ko.sessionAuthModal.test.js

@@ -0,0 +1,38 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import { SHOWN_EVENT, SHOW_EVENT } from './ko.sessionAuthModal';
+import huePubSub from 'utils/huePubSub';
+
+import 'ext/bootstrap.2.3.2.min';
+
+describe('ko.sessionAuthModal.js', () => {
+  it('should render component', async () => {
+    huePubSub.publish(SHOW_EVENT, {
+      session: {
+        properties: []
+      },
+      message: 'hello'
+    });
+
+    await new Promise(resolve => {
+      huePubSub.subscribeOnce(SHOWN_EVENT, resolve);
+    });
+
+    expect(window.document.documentElement.outerHTML).toMatchSnapshot();
+  });
+});

+ 13 - 3
desktop/core/src/desktop/js/ko/components/ko.shareDocModal.js

@@ -22,6 +22,9 @@ import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import HueFileEntry from 'doc/hueFileEntry';
 
+export const SHOW_EVENT = 'doc.show.share.modal';
+export const SHOWN_EVENT = 'doc.share.modal.shown';
+
 // prettier-ignore
 const TEMPLATE = `
   <!-- ko with: document -->
@@ -134,7 +137,7 @@ const getEntry = async params =>
   });
 
 componentUtils.registerComponent('share-doc-modal', undefined, TEMPLATE).then(() => {
-  huePubSub.subscribe('doc.show.share.modal', async params => {
+  huePubSub.subscribe(SHOW_EVENT, async params => {
     const hueFileEntry = await getEntry(params);
 
     let $shareDocMocal = $('#shareDocModal');
@@ -154,11 +157,18 @@ componentUtils.registerComponent('share-doc-modal', undefined, TEMPLATE).then(()
     }
 
     $shareDocMocal = $(
-      '<div id="shareDocModal" data-bind="component: { name: \'share-doc-modal\', params: $data }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
+      '<div id="shareDocModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'share-doc-modal\', params: params }" data-keyboard="true" class="modal hide fade" tabindex="-1"/>'
     );
     $('body').append($shareDocMocal);
 
-    ko.applyBindings(hueFileEntry, $shareDocMocal[0]);
+    const data = {
+      params: hueFileEntry,
+      descendantsComplete: () => {
+        huePubSub.publish(SHOWN_EVENT);
+      }
+    };
+
+    ko.applyBindings(data, $shareDocMocal[0]);
     $shareDocMocal.modal('show');
     $shareDocMocal.on('hide', () => {
       if (isSharedBefore !== hueFileEntry.document().isShared()) {

+ 36 - 0
desktop/core/src/desktop/js/ko/components/ko.shareDocModal.test.js

@@ -0,0 +1,36 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import { SHOWN_EVENT, SHOW_EVENT } from './ko.shareDocModal';
+import huePubSub from 'utils/huePubSub';
+
+import 'ext/bootstrap.2.3.2.min';
+
+describe('ko.shareDocModal.js', () => {
+  it('should render component', async () => {
+    huePubSub.publish(SHOW_EVENT, {
+      document: ko.observable(),
+      loadDocument: () => {}
+    });
+
+    await new Promise(resolve => {
+      huePubSub.subscribeOnce(SHOWN_EVENT, resolve);
+    });
+
+    expect(window.document.documentElement.outerHTML).toMatchSnapshot();
+  });
+});