@atlassian/atlassian-connect-js 5.3.190

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.depcheckrc.json +16 -0
  2. package/.editorconfig +9 -0
  3. package/.envrc +5 -0
  4. package/.eslintignore +12 -0
  5. package/.eslintrc +31 -0
  6. package/.husky/pre-commit +11 -0
  7. package/.lintstagedrc.js +6 -0
  8. package/.netrc +1 -0
  9. package/.npmrc-public +4 -0
  10. package/.nvmrc +1 -0
  11. package/LICENSE +3 -0
  12. package/README.md +281 -0
  13. package/bitbucket-pipelines.yml +33 -0
  14. package/build/bin/bin-helper.js +29 -0
  15. package/build/bin/npm-postinstall.js +9 -0
  16. package/build/configs/append-sourcemapping.js +13 -0
  17. package/build/configs/availabletasks.js +15 -0
  18. package/build/configs/clean.js +12 -0
  19. package/build/configs/concat.js +44 -0
  20. package/build/configs/copy.js +32 -0
  21. package/build/configs/index.js +19 -0
  22. package/build/configs/jshint.js +9 -0
  23. package/build/configs/karma.js +11 -0
  24. package/build/configs/replace.js +10 -0
  25. package/build/configs/requirejs.js +187 -0
  26. package/build/configs/saucelabs-launchers.js +30 -0
  27. package/build/configs/shell.js +24 -0
  28. package/build/configs/uglify.js +15 -0
  29. package/build/configs/watch.js +45 -0
  30. package/build/end.frag +13 -0
  31. package/build/start.frag +18 -0
  32. package/bundlesize.config.json +14 -0
  33. package/dist/connect-host.css +225 -0
  34. package/dist/connect-host.js +13908 -0
  35. package/dist/connect-host.min.css +1 -0
  36. package/dist/iframe-fedramp.js +13663 -0
  37. package/dist/iframe.js +13663 -0
  38. package/dist/legacy-text-colors.css +245 -0
  39. package/dist/surfaces.css +11 -0
  40. package/dist/themes/atlaskit-tokens_dark-future.css +6 -0
  41. package/dist/themes/atlaskit-tokens_dark.css +394 -0
  42. package/dist/themes/atlaskit-tokens_legacy-dark.css +394 -0
  43. package/dist/themes/atlaskit-tokens_legacy-light.css +394 -0
  44. package/dist/themes/atlaskit-tokens_light-future.css +6 -0
  45. package/dist/themes/atlaskit-tokens_light.css +394 -0
  46. package/dist/themes/atlaskit-tokens_shape.css +14 -0
  47. package/dist/themes/atlaskit-tokens_spacing.css +26 -0
  48. package/dist/themes/atlaskit-tokens_typography-adg3.css +25 -0
  49. package/dist/themes/atlaskit-tokens_typography-modernized.css +25 -0
  50. package/dist/themes/atlaskit-tokens_typography-refreshed.css +25 -0
  51. package/gulpfile.js +204 -0
  52. package/memleak-check/app.html +10 -0
  53. package/memleak-check/index.html +31 -0
  54. package/memleak-check/index.js +33 -0
  55. package/package.json +102 -0
  56. package/renovate.json +11 -0
  57. package/security-assistant.yml +2 -0
  58. package/spec/.eslintrc +10 -0
  59. package/spec/config/karma.base.conf.js +200 -0
  60. package/spec/config/karma.conf.js +20 -0
  61. package/spec/fixtures/base_dialog_component_tests.js +180 -0
  62. package/spec/mocks/mock_feature_flag.js +23 -0
  63. package/spec/tests/amd_spec.js +122 -0
  64. package/spec/tests/analytics_dispatcher_spec.js +429 -0
  65. package/spec/tests/analytics_performance_spec.js +41 -0
  66. package/spec/tests/button_spec.js +143 -0
  67. package/spec/tests/dialog_extension_spec.js +59 -0
  68. package/spec/tests/dialog_module_provider_spec.js +499 -0
  69. package/spec/tests/dialog_module_spec.js +395 -0
  70. package/spec/tests/dialog_spec.js +296 -0
  71. package/spec/tests/dialog_webitem_spec.js +183 -0
  72. package/spec/tests/dropdown_module_spec.js +77 -0
  73. package/spec/tests/env_module_spec.js +153 -0
  74. package/spec/tests/events_spec.js +77 -0
  75. package/spec/tests/extension_configuration_options_store_spec.js +25 -0
  76. package/spec/tests/flag_module_provider_spec.js +79 -0
  77. package/spec/tests/flag_module_spec.js +118 -0
  78. package/spec/tests/flag_spec.js +243 -0
  79. package/spec/tests/host-api_spec.js +220 -0
  80. package/spec/tests/iframe-create_spec.js +43 -0
  81. package/spec/tests/iframe_container_spec.js +19 -0
  82. package/spec/tests/iframe_spec.js +265 -0
  83. package/spec/tests/inline_dialog_spec.js +41 -0
  84. package/spec/tests/inline_dialog_webitem_spec.js +331 -0
  85. package/spec/tests/loading_indicator_spec.js +48 -0
  86. package/spec/tests/messages_module_spec.js +108 -0
  87. package/spec/tests/meta_spec.js +71 -0
  88. package/spec/tests/module_args_spec.js +52 -0
  89. package/spec/tests/observe_spec.js +73 -0
  90. package/spec/tests/public_events.spec.js +102 -0
  91. package/spec/tests/scroll_position_spec.js +109 -0
  92. package/spec/tests/theming_spec.js +594 -0
  93. package/spec/tests/util_spec.js +55 -0
  94. package/spec/tests/utils/base64_spec.js +20 -0
  95. package/spec/tests/utils/button_spec.js +11 -0
  96. package/spec/tests/utils/cookie_spec.js +19 -0
  97. package/spec/tests/utils/deprecate_spec.js +22 -0
  98. package/spec/tests/utils/dialog_spec.js +297 -0
  99. package/spec/tests/utils/host_util_spec.js +45 -0
  100. package/spec/tests/utils/iframe_spec.js +41 -0
  101. package/spec/tests/utils/jwt_spec.js +135 -0
  102. package/spec/tests/utils/url_spec.js +37 -0
  103. package/spec/tests/utils/waitUntilReadyNextTick.js +5 -0
  104. package/spec/tests/utils/webitem.js +219 -0
  105. package/spec/tests/webitem_spec.js +131 -0
  106. package/src/css/host/dialog.css +144 -0
  107. package/src/css/host/flags.css +5 -0
  108. package/src/css/host/host.css +64 -0
  109. package/src/css/host/messages.css +12 -0
  110. package/src/css/plugin/legacy-text-colors.less +55 -0
  111. package/src/css/plugin/surfaces.css +11 -0
  112. package/src/host/ACJSFrameworkAdaptor.js +44 -0
  113. package/src/host/actions/analytics_action.js +35 -0
  114. package/src/host/actions/button_actions.js +21 -0
  115. package/src/host/actions/dialog_actions.js +27 -0
  116. package/src/host/actions/dialog_extension_actions.js +25 -0
  117. package/src/host/actions/dom_event_actions.js +32 -0
  118. package/src/host/actions/dropdown_actions.js +12 -0
  119. package/src/host/actions/env_actions.js +57 -0
  120. package/src/host/actions/event_actions.js +33 -0
  121. package/src/host/actions/flag_actions.js +22 -0
  122. package/src/host/actions/iframe_actions.js +27 -0
  123. package/src/host/actions/inline_dialog_actions.js +26 -0
  124. package/src/host/actions/inline_dialog_webitem_actions.js +10 -0
  125. package/src/host/actions/jwt_actions.js +51 -0
  126. package/src/host/actions/loading_indicator_actions.js +10 -0
  127. package/src/host/actions/module_actions.js +14 -0
  128. package/src/host/actions/webitem_actions.js +29 -0
  129. package/src/host/components/button.js +112 -0
  130. package/src/host/components/dialog.js +447 -0
  131. package/src/host/components/dialog_extension.js +106 -0
  132. package/src/host/components/dialog_webitem.js +69 -0
  133. package/src/host/components/flag.js +102 -0
  134. package/src/host/components/iframe.js +130 -0
  135. package/src/host/components/iframe_container.js +38 -0
  136. package/src/host/components/inline_dialog.js +108 -0
  137. package/src/host/components/inline_dialog_webitem.js +157 -0
  138. package/src/host/components/loading_indicator.js +110 -0
  139. package/src/host/components/webitem.js +227 -0
  140. package/src/host/deprecate.js +20 -0
  141. package/src/host/dispatchers/analytics_dispatcher.js +512 -0
  142. package/src/host/dispatchers/event_dispatcher.js +46 -0
  143. package/src/host/dollar.js +8 -0
  144. package/src/host/host-api.js +325 -0
  145. package/src/host/iframe-create.js +7 -0
  146. package/src/host/index.js +108 -0
  147. package/src/host/module-providers.js +13 -0
  148. package/src/host/modules/_featureFlag.js +42 -0
  149. package/src/host/modules/_performance.js +54 -0
  150. package/src/host/modules/analytics.js +17 -0
  151. package/src/host/modules/dialog.js +496 -0
  152. package/src/host/modules/dropdown.js +255 -0
  153. package/src/host/modules/env.js +156 -0
  154. package/src/host/modules/events.js +28 -0
  155. package/src/host/modules/flag.js +170 -0
  156. package/src/host/modules/host.js +19 -0
  157. package/src/host/modules/inline-dialog.js +33 -0
  158. package/src/host/modules/messages.js +309 -0
  159. package/src/host/modules/page.js +29 -0
  160. package/src/host/modules/scroll-position.js +102 -0
  161. package/src/host/modules/theming.js +114 -0
  162. package/src/host/stores/extension_configuration_options_store.js +24 -0
  163. package/src/host/util.js +98 -0
  164. package/src/host/utils/access-narrowing-context.js +18 -0
  165. package/src/host/utils/base64.js +22 -0
  166. package/src/host/utils/button.js +10 -0
  167. package/src/host/utils/cookie.js +14 -0
  168. package/src/host/utils/dialog.js +239 -0
  169. package/src/host/utils/feature-flag.js +27 -0
  170. package/src/host/utils/iframe.js +23 -0
  171. package/src/host/utils/jwt.js +60 -0
  172. package/src/host/utils/observe.js +34 -0
  173. package/src/host/utils/removal-observer.js +30 -0
  174. package/src/host/utils/simplexdm.js +58 -0
  175. package/src/host/utils/url.js +23 -0
  176. package/src/host/utils/webitem.js +214 -0
  177. package/src/plugin/amd.js +115 -0
  178. package/src/plugin/analytics.js +84 -0
  179. package/src/plugin/deprecate.js +16 -0
  180. package/src/plugin/dialog.js +197 -0
  181. package/src/plugin/dollar.js +77 -0
  182. package/src/plugin/events-instance.js +2 -0
  183. package/src/plugin/events.js +246 -0
  184. package/src/plugin/extension_configuration_options_store.js +27 -0
  185. package/src/plugin/featureFlag.js +8 -0
  186. package/src/plugin/index.js +118 -0
  187. package/src/plugin/meta.js +14 -0
  188. package/src/plugin/public-events.js +73 -0
  189. package/src/plugin/theming.js +211 -0
  190. package/src/plugin/util.js +104 -0
@@ -0,0 +1,265 @@
1
+ import IframeComponent from 'src/host/components/iframe';
2
+ import EventDispatcher from 'src/host/dispatchers/event_dispatcher';
3
+ import simpleXDM from 'simple-xdm/host';
4
+ import urlUtil from 'src/host/utils/url';
5
+ import { Flags } from 'src/host/utils/feature-flag';
6
+
7
+ describe('Iframe component', () => {
8
+ function randomWholeNumber(max){
9
+ return Math.floor(Math.random() * max);
10
+ }
11
+
12
+ describe('resize', () =>{
13
+ it('sets dimensions', () => {
14
+ var $mock = $('<div />');
15
+ var width = randomWholeNumber(1000) + 'px';
16
+ var height = randomWholeNumber(1000) + 'px';
17
+ IframeComponent.resize(width, height, $mock);
18
+ expect($mock.css('height')).toEqual(height);
19
+ expect($mock.css('width')).toEqual(width);
20
+ });
21
+
22
+ it('sets dimensions by %', () => {
23
+ var $mock = $('<div />');
24
+ var width = randomWholeNumber(99) + '%';
25
+ var height = randomWholeNumber(99) + '%';
26
+ IframeComponent.resize(width, height, $mock);
27
+ expect($mock.css('height')).toEqual(height);
28
+ expect($mock.css('width')).toEqual(width);
29
+ });
30
+
31
+ it('triggers a resized dom event', () => {
32
+ var $mock = $('<div />');
33
+ var spy = jasmine.createSpy('spy');
34
+ var width = randomWholeNumber(1000) + 'px';
35
+ var height = randomWholeNumber(1000) + 'px';
36
+ $mock.on('resized', spy);
37
+ IframeComponent.resize(width, height, $mock);
38
+ expect(spy.calls.count()).toEqual(1);
39
+ expect(spy.calls.first().args[1]).toEqual({
40
+ height: height,
41
+ width: width
42
+ });
43
+ });
44
+
45
+ it('is triggered by a iframe-resize event', () => {
46
+ var data = {
47
+ $el: $('<div />'),
48
+ width: randomWholeNumber(1000) + 'px',
49
+ height: randomWholeNumber(1000) + 'px'
50
+ };
51
+ spyOn(IframeComponent, 'resize');
52
+ EventDispatcher.dispatch('iframe-resize', data);
53
+ expect(IframeComponent.resize.calls.count()).toEqual(1);
54
+ var args = IframeComponent.resize.calls.first().args;
55
+ expect(args[0]).toEqual(data.width);
56
+ expect(args[1]).toEqual(data.height);
57
+ expect(args[2]).toEqual(data.$el);
58
+ });
59
+ });
60
+
61
+ describe('content resolver', () =>{
62
+ it('setContentResolver sets the content resolver', () => {
63
+ var spy = jasmine.createSpy('spy');
64
+ expect(IframeComponent._contentResolver).not.toEqual(spy);
65
+ IframeComponent.setContentResolver(spy);
66
+ expect(IframeComponent._contentResolver).toEqual(spy);
67
+ });
68
+
69
+ it('sets the content resolver when content-resolver-register-by-extension is triggered', () => {
70
+ var spy = jasmine.createSpy('spy');
71
+ expect(IframeComponent._contentResolver).not.toEqual(spy);
72
+ EventDispatcher.dispatch('content-resolver-register-by-extension', {
73
+ callback: spy
74
+ });
75
+ expect(IframeComponent._contentResolver).toEqual(spy);
76
+ });
77
+
78
+ it('renders an error when content resolver fails', () => {
79
+ var addon_key = 'someaddonkey';
80
+ var key = 'somekey';
81
+ var options = {};
82
+ var errorText = 'some error text';
83
+ var $container = $('<div />');
84
+ var failingResolver = function(){
85
+ return AJS.$.Deferred(function(defer){
86
+ defer.reject({
87
+ addon_key: addon_key,
88
+ key: key,
89
+ options: options
90
+ }, {
91
+ text: errorText
92
+ });
93
+ }).promise();
94
+ };
95
+ IframeComponent.setContentResolver(failingResolver);
96
+ var simpleExtension = IframeComponent.simpleXdmExtension({
97
+ addon_key: addon_key,
98
+ key: key,
99
+ options: options
100
+ }, $container);
101
+ expect($container.text()).toContain(errorText);
102
+ });
103
+ });
104
+
105
+ describe('_simpleXdmCreate', () => {
106
+ it('returns an ID', () => {
107
+ var extension = {
108
+ addon_key: 'some-addon-key',
109
+ key: 'some-module-key',
110
+ url: 'https://www.example.com'
111
+ };
112
+ var simpleExtension = IframeComponent._simpleXdmCreate(extension);
113
+ expect(simpleExtension.id).toEqual(jasmine.stringMatching(new RegExp('^' + extension.addon_key + '__' + extension.key)));
114
+ });
115
+ });
116
+
117
+
118
+ describe('simpleXdmExtension', () => {
119
+ beforeEach(() => {
120
+ IframeComponent._contentResolver = false;
121
+ });
122
+
123
+ it('appends an iframe', () => {
124
+ var extension = {
125
+ addon_key: 'some-addon-key',
126
+ key: 'some-module-key',
127
+ url: 'https://www.example.com'
128
+ };
129
+ var $container = $('<div />');
130
+ var simpleExtension = IframeComponent.simpleXdmExtension(extension, $container);
131
+ expect($container.find('iframe').length).toEqual(1);
132
+ });
133
+
134
+ it('JWT url', () => {
135
+ var extension = {
136
+ addon_key: 'some-addon-key',
137
+ key: 'some-module-key',
138
+ url: 'https://www.example.com/?jwt=abc123'
139
+ };
140
+ var $container = $('<div />');
141
+ spyOn(urlUtil,'hasJwt').and.returnValue(true);
142
+ spyOn(urlUtil,'isJwtExpired').and.returnValue(false);
143
+ IframeComponent.simpleXdmExtension(extension, $container);
144
+ expect($container.find('iframe').length).toEqual(1);
145
+ expect($container.find('iframe').attr('src')).toEqual(extension.url);
146
+ });
147
+
148
+ it('expired JWT', () => {
149
+ var spy = jasmine.createSpy('spy');
150
+ var extension = {
151
+ addon_key: 'some-addon-key',
152
+ key: 'some-module-key',
153
+ url: 'https://www.example.com?jwt=abc123',
154
+ id: 'some-id'
155
+ };
156
+ var $container = $('<div />');
157
+
158
+ spyOn(urlUtil,'hasJwt').and.returnValue(true);
159
+ spyOn(urlUtil,'isJwtExpired').and.returnValue(true);
160
+
161
+ var createdExtension = IframeComponent.simpleXdmExtension($container, extension);
162
+ expect($container.find('iframe').length).toEqual(0);
163
+ });
164
+
165
+ it('triggers an event on bridge established', (done) => {
166
+ var extension = {
167
+ addon_key: 'some-addon-key',
168
+ key: 'some-module-key',
169
+ url: 'https://www.example2.com',
170
+ options: {
171
+ noDom: true,
172
+ }
173
+ };
174
+ var $container = $('<div />');
175
+
176
+ $('body').append($container);
177
+ EventDispatcher.registerOnce('iframe-bridge-established', (data) => {
178
+ expect(data.$el[0].nodeName).toEqual('IFRAME');
179
+ expect(data.extension).toEqual(extension);
180
+ $container.remove();
181
+ done();
182
+ });
183
+ var spy = spyOn(simpleXDM, 'create').and.returnValue({id: 'abc123'});
184
+ IframeComponent.simpleXdmExtension(extension, $container);
185
+ setTimeout(function(){
186
+ spy.calls.first().args[1]();
187
+ }, 300);
188
+ });
189
+
190
+ it('triggers an event on iframe reload', (done) => {
191
+ var extension = {
192
+ addon_key: 'some-addon-key',
193
+ key: 'some-module-key',
194
+ url: 'https://www.example2.com'
195
+ };
196
+ var $container = $('<div />');
197
+
198
+ EventDispatcher.registerOnce('iframe-unload', (data) => {
199
+ expect(data.$el[0].nodeName).toEqual('IFRAME');
200
+ expect(data.extension).toEqual(extension);
201
+ done();
202
+ });
203
+ var spy = spyOn(simpleXDM, 'create').and.returnValue({id: 'abc123'});
204
+ IframeComponent.simpleXdmExtension(extension, $container);
205
+ setTimeout(function(){
206
+ spy.calls.first().args[2]();
207
+ }, 300);
208
+ });
209
+
210
+ });
211
+
212
+ it('render returns an iframe with attributes', () => {
213
+ var attributes = {
214
+ width: '123',
215
+ custom: 'somethingelse'
216
+ };
217
+ var $iframe = IframeComponent.render(attributes);
218
+ expect($iframe.attr('width')).toEqual(attributes.width);
219
+ expect($iframe.attr('custom')).toEqual(attributes.custom);
220
+ expect($iframe[0].nodeName).toEqual('IFRAME');
221
+ });
222
+
223
+ it('renders an iframe with the referrer set to no-referrer', () => {
224
+ var $iframe = IframeComponent.render();
225
+ expect($iframe.attr('referrerpolicy')).toEqual('no-referrer');
226
+ });
227
+
228
+ describe('_appendExtensionError', () => {
229
+ it('displays error message without trace ID when gate display_trace_for_confluence_top_errors is off', () => {
230
+ var $container = $('<div />');
231
+ var traceId = 'trace-123';
232
+ spyOn(Flags, 'getBooleanFeatureFlag').and.callFake((flag) => (
233
+ !(flag === 'display_trace_for_confluence_top_errors')
234
+ ));
235
+ IframeComponent._appendExtensionError($container, 'test error message', traceId);
236
+ expect($container.find('.connect-resolve-error').length).toEqual(1);
237
+ expect($container.find('.error').text()).toContain('Error: The content resolver threw the following error:');
238
+ expect($container.find('.error-trace-id').length).toBe(0);
239
+ });
240
+
241
+ it('displays error message with trace ID when gate display_trace_for_confluence_top_errors is on', () => {
242
+ var $container = $('<div />');
243
+ var traceId = 'trace-123';
244
+ spyOn(Flags, 'getBooleanFeatureFlag').and.callFake((flag) => (
245
+ flag === 'display_trace_for_confluence_top_errors'
246
+ ));
247
+ IframeComponent._appendExtensionError($container, 'test error message', traceId);
248
+ expect($container.find('.connect-resolve-error').length).toEqual(1);
249
+ expect($container.find('.error').text()).toContain('Error: The content resolver threw the following error:');
250
+ expect($container.find('.error-trace-id').text()).toContain('Trace: trace-123');
251
+ });
252
+
253
+ it('displays error message without trace ID when traceId is not provided and gate display_trace_for_confluence_top_errors is on', () => {
254
+ var $container = $('<div />');
255
+ spyOn(Flags, 'getBooleanFeatureFlag').and.callFake((flag) => (
256
+ flag === 'display_trace_for_confluence_top_errors'
257
+ ));
258
+ IframeComponent._appendExtensionError($container, 'test error message');
259
+ expect($container.find('.connect-resolve-error').length).toEqual(1);
260
+ expect($container.find('.error').text()).toContain('Error: The content resolver threw the following error:');
261
+ expect($container.find('.error-trace-id').length).toBe(0);
262
+ });
263
+ });
264
+
265
+ });
@@ -0,0 +1,41 @@
1
+ import InlineDialogComponent from 'src/host/components/inline_dialog';
2
+
3
+ import EventDispatcher from 'src/host/dispatchers/event_dispatcher';
4
+
5
+
6
+ describe('Inline Dialog Component', () => {
7
+
8
+ beforeEach(() => {
9
+ $('.aui-inline-dialog').remove();
10
+ });
11
+
12
+ afterEach(() => {
13
+ });
14
+
15
+ describe('rendering', () => {
16
+ var $content = $('<div />').attr('id', 'inline-dialog-content');
17
+
18
+ it('renders an inline dialog', () => {
19
+ var $inlineDialog = InlineDialogComponent.render({
20
+ id: 'some-id',
21
+ $content: $content
22
+ });
23
+ expect($inlineDialog.hasClass('aui-inline-dialog')).toBe(true);
24
+ expect($inlineDialog.attr('id')).toEqual('inline-dialog-some-id');
25
+ expect($inlineDialog.hide).toEqual(jasmine.any(Function));
26
+ expect($inlineDialog.show).toEqual(jasmine.any(Function));
27
+ expect($inlineDialog.refresh).toEqual(jasmine.any(Function));
28
+ });
29
+
30
+ it('contains the inline dialog content', () => {
31
+ var $inlineDialog = InlineDialogComponent.render({
32
+ id: 'some-id',
33
+ bindTo: $('body'),
34
+ $content: $content
35
+ });
36
+ $inlineDialog.show();
37
+ expect($inlineDialog.find('#inline-dialog-content').length).toEqual(1);
38
+ });
39
+ });
40
+
41
+ });
@@ -0,0 +1,331 @@
1
+ import simpleXDM from 'simple-xdm/host';
2
+ import { InlineDialogWebItem as InlineDialogWebItemConstructor } from 'src/host/components/inline_dialog_webitem';
3
+ import 'src/host/components/inline_dialog';
4
+ import WebItemActions from 'src/host/actions/webitem_actions';
5
+ import EventDispatcher from 'src/host/dispatchers/event_dispatcher';
6
+ import 'src/host/components/iframe_container';
7
+ import jwtActions from 'src/host/actions/jwt_actions';
8
+ import base64 from 'src/host/utils/base64';
9
+ import { waitUntilReadyNextTick } from './utils/waitUntilReadyNextTick';
10
+
11
+ describe('Inline Dialog Webitem', () => {
12
+ var webitemButton;
13
+ var InlineDialogWebitem;
14
+ const classPluginKey = 'ap-plugin-key-my-plugin';
15
+
16
+ function buildJWT(exp){
17
+ const claim = {
18
+ exp: Math.floor(Date.now() / 1000) + exp
19
+ };
20
+ const encodedClaim = base64.encode(JSON.stringify(claim));
21
+ return `alsdjfaj123.${encodedClaim}.khsadlj234`;
22
+ }
23
+
24
+ beforeEach(() => {
25
+ $('.aui-inline-dialog').remove();
26
+ webitemButton = $('<a />').attr('href', 'https://www.example.com?a.x=b#' + encodeURI(JSON.stringify({productCtx:'{"b.c":"d"}'})));
27
+ webitemButton.text('i am a webitem');
28
+ webitemButton.addClass(`ap-inline-dialog ${classPluginKey} ap-module-key-key ap-target-key-key ap-link-webitem`);
29
+ webitemButton.appendTo('body');
30
+
31
+ // We must do it this way as InlineDialogWebItem exports a singleton which is created before this value can be mocked
32
+ InlineDialogWebitem = new InlineDialogWebItemConstructor()
33
+ });
34
+
35
+ afterEach(() => {
36
+ jasmine.clock().uninstall();
37
+ webitemButton.remove();
38
+ if (window && window._AP) {
39
+ delete window._AP.inlineDialogModules;
40
+ }
41
+ });
42
+
43
+ it('getWebItem returns a webitem compatible object', function(){
44
+ var inlineDialogWebitemSpec = InlineDialogWebitem.getWebItem();
45
+ expect(inlineDialogWebitemSpec).toEqual({
46
+ name: 'inline-dialog',
47
+ selector: '.ap-inline-dialog',
48
+ triggers: [ 'mouseenter', 'click' ]
49
+ });
50
+ });
51
+
52
+ describe('rendering', () => {
53
+
54
+ it('renders an inline dialog', (done) => {
55
+ EventDispatcher.registerOnce('after:webitem-invoked:inline-dialog', function(){
56
+ expect($('.aui-inline-dialog').length).toBe(1);
57
+ done();
58
+ });
59
+ waitUntilReadyNextTick(() => {
60
+ $('.ap-inline-dialog').click();
61
+ });
62
+ });
63
+
64
+ it('does not render multiple times for the same extension', (done) => {
65
+ waitUntilReadyNextTick(() => {
66
+ $('.ap-inline-dialog').click();
67
+ expect($('.aui-inline-dialog').length).toBe(1);
68
+ $('.ap-inline-dialog').click();
69
+ expect($('.aui-inline-dialog').length).toBe(1);
70
+ done();
71
+ });
72
+ });
73
+
74
+
75
+ it('passes inline dialog options to component', (done) => {
76
+ var allPossibleOptions = {
77
+ closeOthers: true,
78
+ isRelativeToMouse: true,
79
+ offsetX: '1px',
80
+ offsetY: '1px',
81
+ onHover: true,
82
+ onTop: true,
83
+ persistent: true,
84
+ showDelay: true,
85
+ width: '100px'
86
+ };
87
+
88
+ window._AP = window._AP || {};
89
+ window._AP.inlineDialogModules = {};
90
+ window._AP.inlineDialogModules['my-plugin'] = {
91
+ key: {
92
+ options: allPossibleOptions
93
+ }
94
+ };
95
+
96
+ EventDispatcher.registerOnce('inline-dialog-opened', function(data){
97
+ Object.getOwnPropertyNames(allPossibleOptions).forEach(function(name){
98
+ expect(data.extension.options[name]).toEqual(allPossibleOptions[name]);
99
+ });
100
+ done();
101
+ });
102
+ waitUntilReadyNextTick(() => {
103
+ $('.ap-inline-dialog').click();
104
+ });
105
+ });
106
+
107
+ });
108
+
109
+ describe('triggers', () => {
110
+ beforeEach(() => {
111
+ window._AP = {
112
+ _convertConnectOptions: function(data){
113
+ return {
114
+ options: {
115
+ productContext: JSON.parse(data.productCtx)
116
+ }
117
+ };
118
+ }
119
+ }
120
+ });
121
+
122
+ afterEach(() => {
123
+ delete window._AP._convertConnectOptions;
124
+ });
125
+
126
+ it('is set to be triggered by hover and click', () => {
127
+ expect(InlineDialogWebitem.getWebItem().triggers).toEqual(['mouseenter', 'click']);
128
+ });
129
+
130
+ it('responds to a click event', (done) => {
131
+ var spy = jasmine.createSpy('spy');
132
+ spyOn(WebItemActions, 'webitemInvoked');
133
+ waitUntilReadyNextTick(() => {
134
+ $('.ap-inline-dialog').click();
135
+ expect(WebItemActions.webitemInvoked.calls.count()).toEqual(1);
136
+ done();
137
+ });
138
+ });
139
+
140
+ it('responds to a mouseenter event', (done) => {
141
+ var spy = jasmine.createSpy('spy');
142
+ spyOn(WebItemActions, 'webitemInvoked');
143
+ waitUntilReadyNextTick(() => {
144
+ $('.ap-inline-dialog').trigger('mouseenter');
145
+ expect(WebItemActions.webitemInvoked.calls.count()).toEqual(1);
146
+ done();
147
+ });
148
+ });
149
+
150
+
151
+ it('opens with product context', (done) => {
152
+ EventDispatcher.register('inline-dialog-extension', function(data){
153
+ expect(data.extension.options.productContext).toEqual({
154
+ a: 'b'
155
+ });
156
+ done();
157
+ });
158
+
159
+ jwtActions.registerContentResolver({callback: function(data){
160
+ return jQuery.Deferred(function(defer){
161
+ defer.resolve({
162
+ url: 'http://www.example.com',
163
+ addon_key: data.addon_key,
164
+ key: data.key,
165
+ options: {
166
+ productContext: {
167
+ a: 'b'
168
+ }
169
+ }
170
+ });
171
+ }).promise();
172
+ }});
173
+
174
+ InlineDialogWebitem.opened({
175
+ $el: $('<div />'),
176
+ extension: {
177
+ addon_key: 'a-key',
178
+ key: 'key'
179
+ }
180
+ });
181
+ });
182
+
183
+ it('invokes with product context', (done) => {
184
+ var spy = jasmine.createSpy('spy');
185
+ spyOn(WebItemActions, 'webitemInvoked');
186
+ waitUntilReadyNextTick(() => {
187
+ $('.ap-inline-dialog').trigger('click');
188
+ var extensionObj = WebItemActions.webitemInvoked.calls.first().args[1].extension;
189
+ expect(extensionObj.options.productContext).toEqual({'b.c': 'd'});
190
+ done();
191
+ });
192
+ });
193
+
194
+ it('only calls the content resolver once per add-on', (done) => {
195
+ var spy = jasmine.createSpy('spy');
196
+
197
+ jwtActions.registerContentResolver({callback: function(data){
198
+ spy();
199
+ // if you don't return a promise this regression test will always pass
200
+ return jQuery.Deferred(function(defer){
201
+ defer.resolve({
202
+ url: 'http://www.example.com',
203
+ addon_key: data.addon_key,
204
+ key: data.key,
205
+ options: {
206
+ productContext: {
207
+ a: 'b'
208
+ }
209
+ }
210
+ });
211
+ }).promise();
212
+ }});
213
+ waitUntilReadyNextTick(() => {
214
+ $('.ap-inline-dialog').trigger('click');
215
+ setTimeout(function(){
216
+ expect(spy.calls.count()).toEqual(1);
217
+ done();
218
+ }, 300);
219
+ });
220
+ });
221
+
222
+ it('reuses iframe if JWT has not timed out', () => {
223
+ const jwt = buildJWT(100);
224
+
225
+ jwtActions.registerContentResolver({callback: function(data){
226
+ return jQuery.Deferred(function(defer){
227
+ defer.resolve({
228
+ url: 'http://www.example.com?jwt=' + jwt,
229
+ addon_key: data.addon_key,
230
+ key: data.key,
231
+ options: {
232
+ productContext: {
233
+ a: 'b'
234
+ }
235
+ }
236
+ });
237
+ }).promise();
238
+ }});
239
+
240
+ let data = {
241
+ $el: $('<div />'),
242
+ extension: {
243
+ addon_key: 'a-key',
244
+ key: 'key'
245
+ }
246
+ };
247
+ expect(InlineDialogWebitem.opened(data)).toEqual(true);
248
+ expect(InlineDialogWebitem.opened(data)).toEqual(false);
249
+ });
250
+
251
+ it('gets new JWT if expired', () => {
252
+ jasmine.clock().install();
253
+ const jwt = buildJWT(0);
254
+ jwtActions.registerContentResolver({callback: function(data){
255
+ return jQuery.Deferred(function(defer){
256
+ defer.resolve({
257
+ url: 'http://www.example.com?jwt=' + jwt,
258
+ addon_key: data.addon_key,
259
+ key: data.key,
260
+ options: {
261
+ productContext: {
262
+ a: 'b'
263
+ }
264
+ }
265
+ });
266
+ }).promise();
267
+ }});
268
+
269
+ let data = {
270
+ $el: $('<div />'),
271
+ extension: {
272
+ addon_key: 'a-key',
273
+ key: 'key'
274
+ }
275
+ };
276
+ expect(InlineDialogWebitem.opened(data)).toEqual(true);
277
+ jasmine.clock().tick(10000000);
278
+ expect(InlineDialogWebitem.opened(data)).toEqual(true);
279
+ jasmine.clock().uninstall();
280
+ });
281
+
282
+ it('reuses iframe if url is known and no jwt', () => {
283
+ jwtActions.registerContentResolver({callback: function(data){
284
+ return jQuery.Deferred(function(defer){
285
+ defer.resolve({
286
+ url: 'http://www.example.com',
287
+ addon_key: data.addon_key,
288
+ key: data.key,
289
+ options: {
290
+ productContext: {
291
+ a: 'b'
292
+ }
293
+ }
294
+ });
295
+ }).promise();
296
+ }});
297
+
298
+ let data = {
299
+ $el: $('<div />'),
300
+ extension: {
301
+ addon_key: 'a-key',
302
+ key: 'key',
303
+ url: 'http://www.example.com'
304
+ }
305
+ };
306
+ expect(InlineDialogWebitem.opened(data)).toEqual(true);
307
+ expect(InlineDialogWebitem.opened(data)).toEqual(false);
308
+ });
309
+ });
310
+
311
+ it('drops events for confluence com.addonengine.analytics plugin until analytics module is loaded', (done) => {
312
+ spyOn(WebItemActions, 'webitemInvoked');
313
+ waitUntilReadyNextTick(() => {
314
+ // With a regular plugin key, the click should pass through uninterrupted
315
+ $(webitemButton).trigger('click');
316
+ expect(WebItemActions.webitemInvoked.calls.count()).toEqual(1);
317
+
318
+ // With the special plugin key, the click should be blocked if the analytics module isn't loaded
319
+ $(webitemButton).removeClass(classPluginKey).addClass('ap-plugin-key-com.addonengine.analytics');
320
+ $(webitemButton).trigger('click');
321
+ expect(WebItemActions.webitemInvoked.calls.count()).toEqual(1);
322
+
323
+ // When we load the analytics module the click passes through again
324
+ simpleXDM.defineModule('analytics', {});
325
+ $(webitemButton).trigger('click');
326
+ expect(WebItemActions.webitemInvoked.calls.count()).toEqual(2);
327
+ done();
328
+ });
329
+ });
330
+
331
+ });
@@ -0,0 +1,48 @@
1
+ import LoadingIndicatorComponent from 'src/host/components/loading_indicator';
2
+ import EventDispatcher from 'src/host/dispatchers/event_dispatcher';
3
+
4
+
5
+ describe('Loading indicator component', () => {
6
+ var loadingIndicator;
7
+ var $container;
8
+ var extensionId = 'sd32ndiu2idni';
9
+
10
+ beforeEach(() => {
11
+ $container = $('<div />').addClass('loading-indicator-container');
12
+ $container.appendTo('body');
13
+ });
14
+
15
+ afterEach(() => {
16
+ $container.remove();
17
+ });
18
+
19
+ it('will render', () =>{
20
+ var loadingIndicator = LoadingIndicatorComponent.render();
21
+ expect(loadingIndicator.length).toEqual(1);
22
+ expect(loadingIndicator.find('.small-spinner').length).toBe(1);
23
+ });
24
+
25
+ it('will hide', () => {
26
+ var loadingIndicator = LoadingIndicatorComponent.render();
27
+ $container.append(loadingIndicator);
28
+ expect(loadingIndicator.is(':visible')).toBe(true);
29
+ LoadingIndicatorComponent.hide($container, extensionId);
30
+ expect(loadingIndicator.is(':visible')).toBe(false);
31
+ });
32
+
33
+ it('will timeout', () => {
34
+ var loadingIndicator = LoadingIndicatorComponent.render();
35
+ $container.append(loadingIndicator);
36
+ LoadingIndicatorComponent.timeout($container, extensionId);
37
+ expect($container.find('a.ap-btn-cancel').length).toEqual(1);
38
+ });
39
+
40
+ it('will cancel', () => {
41
+ var loadingIndicator = LoadingIndicatorComponent.render();
42
+ $container.append(loadingIndicator);
43
+ LoadingIndicatorComponent.cancelled($container, extensionId);
44
+ expect($container.find('a.ap-btn-cancel').length).toEqual(0);
45
+ expect($container.text().length > 1).toBe(true);
46
+ });
47
+
48
+ });