cozy-harvest-lib 32.4.4 → 32.5.0
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.
- package/CHANGELOG.md +10 -0
- package/dist/components/OAuthService.js +0 -69
- package/dist/components/OAuthService.spec.js +9 -95
- package/dist/components/Popup.js +0 -21
- package/dist/components/PopupService.js +0 -57
- package/dist/components/PopupService.spec.js +14 -107
- package/package.json +4 -4
- package/src/components/OAuthService.js +0 -56
- package/src/components/OAuthService.spec.js +1 -69
- package/src/components/Popup.jsx +0 -21
- package/src/components/PopupService.js +1 -61
- package/src/components/PopupService.spec.js +0 -77
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [32.5.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@32.4.5...cozy-harvest-lib@32.5.0) (2025-09-01)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- Remove usage of useless isMobileApp method ([e26ce64](https://github.com/cozy/cozy-libs/commit/e26ce64c2a6231aaab09ba04e28e31a1262bd0fd))
|
|
11
|
+
|
|
12
|
+
## [32.4.5](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@32.4.4...cozy-harvest-lib@32.4.5) (2025-08-21)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package cozy-harvest-lib
|
|
15
|
+
|
|
6
16
|
## [32.4.4](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@32.4.3...cozy-harvest-lib@32.4.4) (2025-08-19)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package cozy-harvest-lib
|
|
@@ -10,45 +10,6 @@ var OAUTH_POPUP_WIDTH = 800;
|
|
|
10
10
|
export var OAUTH_SERVICE_CLOSED = 'CLOSED';
|
|
11
11
|
export var OAUTH_SERVICE_ERROR = 'ERROR';
|
|
12
12
|
export var OAUTH_SERVICE_OK = 'OK';
|
|
13
|
-
/**
|
|
14
|
-
* Monitor URL changes for mobile apps and in app browsers
|
|
15
|
-
* @param {import('cozy-client/types/types').KonnectorsDoctype} konnector
|
|
16
|
-
*
|
|
17
|
-
* The provider redirect us to something like : oauthcallback.mycozy.cloud/?account=A&state=b
|
|
18
|
-
* So we listen to the URL change with these informations, and we try to handle it.
|
|
19
|
-
* It works well on iOS or when we are logged on the same device on the web, but it will
|
|
20
|
-
* fail on Android if we're ne logged in the browser. Why ?
|
|
21
|
-
* Our oauthcallback.mycozy.cloud/?account=A&state=b checks if we're logged, if not the server
|
|
22
|
-
* sends an http redirect.
|
|
23
|
-
* On Android, the 'loadstart' event is not dispatched by the browser when it get a redirect.
|
|
24
|
-
* The inAppBrowser follows the URL and arrive on :
|
|
25
|
-
* https://my.mycozy.cloud/auth?redirect=https://home.cozy.cloud/?account=a&state=b
|
|
26
|
-
* So if we don't have account & state searchParams we have to check if we've a redirect searchParams
|
|
27
|
-
* init it and search if we have inside this url an account and state params
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
function handleUrlChange(konnector) {
|
|
31
|
-
return function (resolve) {
|
|
32
|
-
return function (event) {
|
|
33
|
-
var url = event.url;
|
|
34
|
-
var oAuthData = extractOAuthDataFromUrl(url);
|
|
35
|
-
|
|
36
|
-
if (oAuthData) {
|
|
37
|
-
return handleOAuthData(oAuthData, konnector, resolve);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
var redirect = url.searchParams.get('redirect');
|
|
41
|
-
|
|
42
|
-
if (redirect) {
|
|
43
|
-
var redirectOAuthData = extractOAuthDataFromUrl(new URL(redirect));
|
|
44
|
-
|
|
45
|
-
if (redirectOAuthData) {
|
|
46
|
-
return handleOAuthData(redirectOAuthData, konnector, resolve);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
13
|
/**
|
|
53
14
|
* Handles OAuth data. OAuth data may be provided by different way:
|
|
54
15
|
* * realtime message from web apps (see handleMessage)
|
|
@@ -60,7 +21,6 @@ function handleUrlChange(konnector) {
|
|
|
60
21
|
* @returns <void>
|
|
61
22
|
*/
|
|
62
23
|
|
|
63
|
-
|
|
64
24
|
function handleOAuthData(data, konnector, resolve) {
|
|
65
25
|
if (!checkOAuthData(konnector, data)) return;
|
|
66
26
|
|
|
@@ -87,34 +47,6 @@ function handleOAuthData(data, konnector, resolve) {
|
|
|
87
47
|
* @property {String} [finalLocation] - final query string added to the redirected url
|
|
88
48
|
*/
|
|
89
49
|
|
|
90
|
-
/**
|
|
91
|
-
* Extract OAuthData from url search params if any
|
|
92
|
-
*
|
|
93
|
-
* @param {URL} url
|
|
94
|
-
* @returns {OAuthData|null} OAuthData
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
function extractOAuthDataFromUrl(url) {
|
|
99
|
-
var account = url.searchParams.get('account');
|
|
100
|
-
var state = url.searchParams.get('state');
|
|
101
|
-
var error = url.searchParams.get('error');
|
|
102
|
-
|
|
103
|
-
if (account && state) {
|
|
104
|
-
var oAuthData = {
|
|
105
|
-
key: account,
|
|
106
|
-
oAuthStateKey: state
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
if (error) {
|
|
110
|
-
oAuthData.error = error;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return oAuthData;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
50
|
/**
|
|
119
51
|
* @typedef OAuthServiceResult
|
|
120
52
|
* @property {"OK"|"CLOSED"|"ERROR"} result
|
|
@@ -443,7 +375,6 @@ export function openOAuthWindow(_ref5) {
|
|
|
443
375
|
title: title || '',
|
|
444
376
|
width: OAUTH_POPUP_WIDTH,
|
|
445
377
|
height: OAUTH_POPUP_HEIGHT,
|
|
446
|
-
handleUrlChange: handleUrlChange(konnector),
|
|
447
378
|
registerRealtime: registerRealtime({
|
|
448
379
|
konnector: konnector,
|
|
449
380
|
client: client
|
|
@@ -9,7 +9,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
9
9
|
import util from 'util';
|
|
10
10
|
import { openOAuthWindow, OAUTH_SERVICE_OK } from 'components/OAuthService';
|
|
11
11
|
import MicroEE from 'microee';
|
|
12
|
-
import {
|
|
12
|
+
import { isFlagshipApp } from 'cozy-device-helper';
|
|
13
13
|
import { OAUTH_REALTIME_CHANNEL, terminateOAuth } from '../helpers/oauth';
|
|
14
14
|
import PopupMock from '../helpers/windowWrapperMocks';
|
|
15
15
|
jest.mock('../helpers/oauth', function () {
|
|
@@ -69,7 +69,6 @@ var client = {
|
|
|
69
69
|
};
|
|
70
70
|
jest.mock('cozy-device-helper', function () {
|
|
71
71
|
return {
|
|
72
|
-
isMobileApp: jest.fn(),
|
|
73
72
|
isFlagshipApp: jest.fn()
|
|
74
73
|
};
|
|
75
74
|
});
|
|
@@ -156,94 +155,15 @@ describe('OAuthService', function () {
|
|
|
156
155
|
}, _callee);
|
|
157
156
|
})));
|
|
158
157
|
});
|
|
159
|
-
describe('with intentsApi', function () {
|
|
160
|
-
beforeEach(function () {
|
|
161
|
-
isMobileApp.mockReturnValue(true);
|
|
162
|
-
});
|
|
163
|
-
it('should render', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
164
|
-
var closePromise, intentsApi, promise, result;
|
|
165
|
-
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
166
|
-
while (1) {
|
|
167
|
-
switch (_context2.prev = _context2.next) {
|
|
168
|
-
case 0:
|
|
169
|
-
closePromise = new Promise(function () {});
|
|
170
|
-
intentsApi = {
|
|
171
|
-
fetchSessionCode: jest.fn().mockResolvedValue('somesessioncode'),
|
|
172
|
-
showInAppBrowser: jest.fn().mockImplementation(function () {
|
|
173
|
-
return closePromise;
|
|
174
|
-
}),
|
|
175
|
-
closeInAppBrowser: jest.fn().mockResolvedValue()
|
|
176
|
-
};
|
|
177
|
-
promise = openOAuthWindow({
|
|
178
|
-
client: client,
|
|
179
|
-
konnector: {
|
|
180
|
-
slug: 'ameli'
|
|
181
|
-
},
|
|
182
|
-
redirectSlug: 'home',
|
|
183
|
-
extraParams: {
|
|
184
|
-
extraParam1: 'extraParamValue'
|
|
185
|
-
},
|
|
186
|
-
title: 'popup title',
|
|
187
|
-
account: {
|
|
188
|
-
_id: 'someaccountid'
|
|
189
|
-
},
|
|
190
|
-
intentsApi: intentsApi
|
|
191
|
-
});
|
|
192
|
-
expect(isPending(promise)).toBe(true);
|
|
193
|
-
expect(terminateOAuth).not.toHaveBeenCalled();
|
|
194
|
-
_context2.next = 7;
|
|
195
|
-
return sleep(100);
|
|
196
|
-
|
|
197
|
-
case 7:
|
|
198
|
-
// lets time to show inAppBrowser before realtime event occurs
|
|
199
|
-
mockCozyRealtime.emitRealtimeEvent('notified', 'io.cozy.accounts', OAUTH_REALTIME_CHANNEL, {
|
|
200
|
-
_id: 'oauth-popup',
|
|
201
|
-
data: {
|
|
202
|
-
error: null,
|
|
203
|
-
finalLocation: 'connection_id=194&state=someuuid',
|
|
204
|
-
key: null,
|
|
205
|
-
oAuthStateKey: 'someuuid'
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
_context2.next = 10;
|
|
209
|
-
return promise;
|
|
210
|
-
|
|
211
|
-
case 10:
|
|
212
|
-
result = _context2.sent;
|
|
213
|
-
expect(result).toStrictEqual({
|
|
214
|
-
result: OAUTH_SERVICE_OK,
|
|
215
|
-
key: null,
|
|
216
|
-
data: {
|
|
217
|
-
error: null,
|
|
218
|
-
finalLocation: 'connection_id=194&state=someuuid',
|
|
219
|
-
key: null,
|
|
220
|
-
oAuthStateKey: 'someuuid'
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
expect(terminateOAuth).toHaveBeenCalled();
|
|
224
|
-
expect(intentsApi.fetchSessionCode).toHaveBeenCalledTimes(1);
|
|
225
|
-
expect(intentsApi.showInAppBrowser).toHaveBeenCalled(); // TODO test url mock nonce
|
|
226
|
-
|
|
227
|
-
expect(intentsApi.closeInAppBrowser).toHaveBeenCalled();
|
|
228
|
-
|
|
229
|
-
case 16:
|
|
230
|
-
case "end":
|
|
231
|
-
return _context2.stop();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}, _callee2);
|
|
235
|
-
})));
|
|
236
|
-
});
|
|
237
158
|
describe('on web', function () {
|
|
238
159
|
beforeEach(function () {
|
|
239
|
-
isMobileApp.mockReturnValue(false);
|
|
240
160
|
isFlagshipApp.mockReturnValue(false);
|
|
241
161
|
});
|
|
242
|
-
it('should render', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
162
|
+
it('should render', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
243
163
|
var promise, result;
|
|
244
|
-
return _regeneratorRuntime.wrap(function
|
|
164
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
245
165
|
while (1) {
|
|
246
|
-
switch (
|
|
166
|
+
switch (_context2.prev = _context2.next) {
|
|
247
167
|
case 0:
|
|
248
168
|
promise = openOAuthWindow({
|
|
249
169
|
client: client,
|
|
@@ -270,11 +190,11 @@ describe('OAuthService', function () {
|
|
|
270
190
|
oAuthStateKey: 'someuuid'
|
|
271
191
|
}
|
|
272
192
|
});
|
|
273
|
-
|
|
193
|
+
_context2.next = 6;
|
|
274
194
|
return promise;
|
|
275
195
|
|
|
276
196
|
case 6:
|
|
277
|
-
result =
|
|
197
|
+
result = _context2.sent;
|
|
278
198
|
expect(result).toStrictEqual({
|
|
279
199
|
result: OAUTH_SERVICE_OK,
|
|
280
200
|
key: null,
|
|
@@ -289,10 +209,10 @@ describe('OAuthService', function () {
|
|
|
289
209
|
|
|
290
210
|
case 9:
|
|
291
211
|
case "end":
|
|
292
|
-
return
|
|
212
|
+
return _context2.stop();
|
|
293
213
|
}
|
|
294
214
|
}
|
|
295
|
-
},
|
|
215
|
+
}, _callee2);
|
|
296
216
|
})));
|
|
297
217
|
});
|
|
298
218
|
});
|
|
@@ -300,10 +220,4 @@ describe('OAuthService', function () {
|
|
|
300
220
|
|
|
301
221
|
var isPending = function isPending(promise) {
|
|
302
222
|
return util.inspect(promise).includes('pending');
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
function sleep(ms) {
|
|
306
|
-
return new Promise(function (resolve) {
|
|
307
|
-
return setTimeout(resolve, ms);
|
|
308
|
-
});
|
|
309
|
-
}
|
|
223
|
+
};
|
package/dist/components/Popup.js
CHANGED
|
@@ -11,7 +11,6 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
|
|
|
11
11
|
|
|
12
12
|
import PropTypes from 'prop-types';
|
|
13
13
|
import { PureComponent } from 'react';
|
|
14
|
-
import { isMobileApp } from 'cozy-device-helper';
|
|
15
14
|
/**
|
|
16
15
|
* Customized function to get dimensions and position for a centered
|
|
17
16
|
* popup window
|
|
@@ -73,24 +72,6 @@ export var Popup = /*#__PURE__*/function (_PureComponent) {
|
|
|
73
72
|
value: function componentWillUnmount() {
|
|
74
73
|
this.killPopup();
|
|
75
74
|
}
|
|
76
|
-
}, {
|
|
77
|
-
key: "addListeners",
|
|
78
|
-
value: function addListeners(popup) {
|
|
79
|
-
// rest of instructions only on mobile app
|
|
80
|
-
if (!isMobileApp()) return;
|
|
81
|
-
popup.addEventListener('loadstart', this.handleUrlChange);
|
|
82
|
-
popup.addEventListener('exit', this.handleClose);
|
|
83
|
-
}
|
|
84
|
-
}, {
|
|
85
|
-
key: "removeListeners",
|
|
86
|
-
value: function removeListeners(popup) {
|
|
87
|
-
// rest of instructions only if popup is still opened
|
|
88
|
-
if (popup.closed) return; // rest of instructions only on mobile app
|
|
89
|
-
|
|
90
|
-
if (!isMobileApp()) return;
|
|
91
|
-
popup.removeEventListener('loadstart', this.handleUrlChange);
|
|
92
|
-
popup.removeEventListener('exit', this.handleClose);
|
|
93
|
-
}
|
|
94
75
|
}, {
|
|
95
76
|
key: "handleClose",
|
|
96
77
|
value: function handleClose(popup) {
|
|
@@ -131,7 +112,6 @@ export var Popup = /*#__PURE__*/function (_PureComponent) {
|
|
|
131
112
|
popup.focus();
|
|
132
113
|
}
|
|
133
114
|
|
|
134
|
-
this.addListeners(popup);
|
|
135
115
|
this.startMonitoringClosing(popup);
|
|
136
116
|
this.setState({
|
|
137
117
|
popup: popup
|
|
@@ -141,7 +121,6 @@ export var Popup = /*#__PURE__*/function (_PureComponent) {
|
|
|
141
121
|
key: "killPopup",
|
|
142
122
|
value: function killPopup() {
|
|
143
123
|
var popup = this.state.popup;
|
|
144
|
-
this.removeListeners(popup);
|
|
145
124
|
this.stopMonitoringClosing();
|
|
146
125
|
if (!popup.closed) popup.close();
|
|
147
126
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { isMobileApp } from 'cozy-device-helper';
|
|
3
2
|
import { popupCenter } from './Popup';
|
|
4
3
|
import { windowOpen } from '../helpers/windowWrapper';
|
|
5
4
|
/**
|
|
@@ -15,7 +14,6 @@ import { windowOpen } from '../helpers/windowWrapper';
|
|
|
15
14
|
* @param {String} options.title - Title of the popup
|
|
16
15
|
* @param {Number} options.width - Width of the popup
|
|
17
16
|
* @param {Number} options.height - Height of the popup
|
|
18
|
-
* @param {Function} options.handleUrlChange - Function to call when popup url changes
|
|
19
17
|
* @param {Function} options.registerRealtime - Function to call to register realtime events
|
|
20
18
|
* @returns {Promise<OpenWindowResult>}
|
|
21
19
|
*/
|
|
@@ -25,7 +23,6 @@ export function openWindow(_ref) {
|
|
|
25
23
|
title = _ref.title,
|
|
26
24
|
width = _ref.width,
|
|
27
25
|
height = _ref.height,
|
|
28
|
-
handleUrlChange = _ref.handleUrlChange,
|
|
29
26
|
registerRealtime = _ref.registerRealtime;
|
|
30
27
|
|
|
31
28
|
var _popupCenter = popupCenter(width, height),
|
|
@@ -49,10 +46,6 @@ export function openWindow(_ref) {
|
|
|
49
46
|
var unregisterRealtime;
|
|
50
47
|
|
|
51
48
|
var cleanAndResolve = function cleanAndResolve(data) {
|
|
52
|
-
if (handleUrlChange) {
|
|
53
|
-
removeListeners(popup, handleUrlChange);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
49
|
stopMonitoringClosing(checkClosedInterval);
|
|
57
50
|
|
|
58
51
|
if (registerRealtime) {
|
|
@@ -63,10 +56,6 @@ export function openWindow(_ref) {
|
|
|
63
56
|
resolve(data);
|
|
64
57
|
};
|
|
65
58
|
|
|
66
|
-
if (handleUrlChange) {
|
|
67
|
-
addListeners(popup, cleanAndResolve, handleUrlChange);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
59
|
if (registerRealtime) {
|
|
71
60
|
unregisterRealtime = registerRealtime(cleanAndResolve);
|
|
72
61
|
}
|
|
@@ -113,50 +102,4 @@ function startMonitoringClosing(popup, resolve) {
|
|
|
113
102
|
|
|
114
103
|
function stopMonitoringClosing(checkClosedInterval) {
|
|
115
104
|
clearInterval(checkClosedInterval);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Removes all listeners
|
|
119
|
-
*
|
|
120
|
-
* @param {Object} popup - Current popup
|
|
121
|
-
* @param {Function} handleUrlChange - handleUrlChange function not to listen anymore
|
|
122
|
-
* @returns <void>
|
|
123
|
-
*/
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
function removeListeners(popup, handleUrlChange) {
|
|
127
|
-
// rest of instructions only if popup is still opened
|
|
128
|
-
if (popup.closed) return; // rest of instructions only on mobile app
|
|
129
|
-
|
|
130
|
-
if (!isMobileApp()) return;
|
|
131
|
-
popup.removeEventListener('loadstart', handleUrlChange);
|
|
132
|
-
popup.removeEventListener('exit', handleClose);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Add listeners
|
|
136
|
-
*
|
|
137
|
-
* @param {Object} popup
|
|
138
|
-
* @param {Function} resolve
|
|
139
|
-
* @param {Function} handleUrlChange
|
|
140
|
-
* @returns <void>
|
|
141
|
-
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
function addListeners(popup, resolve, handleUrlChange) {
|
|
145
|
-
// rest of instructions only on mobile app
|
|
146
|
-
if (!isMobileApp()) return;
|
|
147
|
-
popup.addEventListener('loadstart', handleUrlChange(resolve));
|
|
148
|
-
popup.addEventListener('exit', handleClose(resolve));
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Function called when the popup is closed
|
|
152
|
-
*
|
|
153
|
-
* @param {Function} resolve
|
|
154
|
-
* @returns {Function}
|
|
155
|
-
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
function handleClose(resolve) {
|
|
159
|
-
return function (popup) {
|
|
160
|
-
resolve(popup);
|
|
161
|
-
};
|
|
162
105
|
}
|
|
@@ -2,7 +2,6 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
|
2
2
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
3
3
|
import util from 'util';
|
|
4
4
|
import { openWindow } from 'components/PopupService';
|
|
5
|
-
import { isMobileApp } from 'cozy-device-helper';
|
|
6
5
|
import PopupMock from '../helpers/windowWrapperMocks';
|
|
7
6
|
var mockPopup = new PopupMock();
|
|
8
7
|
jest.mock('../helpers/windowWrapper', function () {
|
|
@@ -12,15 +11,8 @@ jest.mock('../helpers/windowWrapper', function () {
|
|
|
12
11
|
})
|
|
13
12
|
};
|
|
14
13
|
});
|
|
15
|
-
jest.mock('cozy-device-helper', function () {
|
|
16
|
-
return {
|
|
17
|
-
isMobileApp: jest.fn()
|
|
18
|
-
};
|
|
19
|
-
});
|
|
20
14
|
describe('PopupService', function () {
|
|
21
15
|
describe('openWindow', function () {
|
|
22
|
-
var handleUrlChange = jest.fn();
|
|
23
|
-
var handleUrlChangeWrapper = jest.fn().mockReturnValue(handleUrlChange);
|
|
24
16
|
var unregisterRealtime = jest.fn();
|
|
25
17
|
var registerRealtime = jest.fn().mockReturnValue(unregisterRealtime);
|
|
26
18
|
beforeEach(function () {
|
|
@@ -29,47 +21,34 @@ describe('PopupService', function () {
|
|
|
29
21
|
afterEach(function () {
|
|
30
22
|
jest.clearAllMocks();
|
|
31
23
|
});
|
|
32
|
-
describe('on
|
|
33
|
-
beforeEach(function () {
|
|
34
|
-
isMobileApp.mockReturnValue(true);
|
|
35
|
-
});
|
|
24
|
+
describe('on web', function () {
|
|
36
25
|
it('should render', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
37
26
|
var promise, result;
|
|
38
27
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
39
28
|
while (1) {
|
|
40
29
|
switch (_context.prev = _context.next) {
|
|
41
30
|
case 0:
|
|
42
|
-
promise = openWindow(
|
|
43
|
-
url: 'http://cozy.tools',
|
|
44
|
-
title: 'SOME_TITLE',
|
|
45
|
-
width: '20',
|
|
46
|
-
height: '30',
|
|
47
|
-
handleUrlChange: handleUrlChangeWrapper
|
|
48
|
-
});
|
|
31
|
+
promise = openWindow('http://cozy.tools', 'SOME_TITLE', 'SOME_OPTIONS');
|
|
49
32
|
expect(mockPopup.focus).toHaveBeenCalled();
|
|
50
|
-
expect(mockPopup.addEventListener).toHaveBeenCalledWith('loadstart', expect.anything());
|
|
51
|
-
expect(mockPopup.addEventListener).toHaveBeenCalledWith('exit', expect.anything());
|
|
52
33
|
expect(isPending(promise)).toBeTruthy();
|
|
53
|
-
mockPopup.
|
|
54
|
-
|
|
55
|
-
});
|
|
56
|
-
_context.next = 8;
|
|
34
|
+
mockPopup.close();
|
|
35
|
+
_context.next = 6;
|
|
57
36
|
return promise;
|
|
58
37
|
|
|
59
|
-
case
|
|
38
|
+
case 6:
|
|
60
39
|
result = _context.sent;
|
|
61
40
|
expect(result).toStrictEqual({
|
|
62
|
-
|
|
41
|
+
result: 'CLOSED'
|
|
63
42
|
});
|
|
64
43
|
|
|
65
|
-
case
|
|
44
|
+
case 8:
|
|
66
45
|
case "end":
|
|
67
46
|
return _context.stop();
|
|
68
47
|
}
|
|
69
48
|
}
|
|
70
49
|
}, _callee);
|
|
71
50
|
})));
|
|
72
|
-
it('should
|
|
51
|
+
it('should accept registerRealtime parameter', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
73
52
|
var promise, result;
|
|
74
53
|
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
75
54
|
while (1) {
|
|
@@ -80,100 +59,28 @@ describe('PopupService', function () {
|
|
|
80
59
|
title: 'SOME_TITLE',
|
|
81
60
|
width: '20',
|
|
82
61
|
height: '30',
|
|
83
|
-
handleUrlChange: handleUrlChangeWrapper
|
|
84
|
-
});
|
|
85
|
-
expect(isPending(promise)).toBeTruthy();
|
|
86
|
-
mockPopup.emit('loadstart', 'HELLO');
|
|
87
|
-
expect(handleUrlChangeWrapper).toHaveBeenCalled();
|
|
88
|
-
expect(handleUrlChange).toHaveBeenCalledWith('HELLO');
|
|
89
|
-
mockPopup.emit('exit', {
|
|
90
|
-
POPUP: 'data'
|
|
91
|
-
});
|
|
92
|
-
_context2.next = 8;
|
|
93
|
-
return promise;
|
|
94
|
-
|
|
95
|
-
case 8:
|
|
96
|
-
result = _context2.sent;
|
|
97
|
-
expect(result).toStrictEqual({
|
|
98
|
-
POPUP: 'data'
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
case 10:
|
|
102
|
-
case "end":
|
|
103
|
-
return _context2.stop();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}, _callee2);
|
|
107
|
-
})));
|
|
108
|
-
});
|
|
109
|
-
describe('on web', function () {
|
|
110
|
-
beforeEach(function () {
|
|
111
|
-
isMobileApp.mockReturnValue(false);
|
|
112
|
-
});
|
|
113
|
-
it('should render', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
|
|
114
|
-
var promise, result;
|
|
115
|
-
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
116
|
-
while (1) {
|
|
117
|
-
switch (_context3.prev = _context3.next) {
|
|
118
|
-
case 0:
|
|
119
|
-
promise = openWindow('http://cozy.tools', 'SOME_TITLE', 'SOME_OPTIONS');
|
|
120
|
-
expect(mockPopup.focus).toHaveBeenCalled();
|
|
121
|
-
expect(mockPopup.addEventListener).not.toHaveBeenCalledWith('loadstart', expect.anything());
|
|
122
|
-
expect(mockPopup.addEventListener).not.toHaveBeenCalledWith('exit', expect.anything());
|
|
123
|
-
expect(isPending(promise)).toBeTruthy();
|
|
124
|
-
mockPopup.close();
|
|
125
|
-
_context3.next = 8;
|
|
126
|
-
return promise;
|
|
127
|
-
|
|
128
|
-
case 8:
|
|
129
|
-
result = _context3.sent;
|
|
130
|
-
expect(result).toStrictEqual({
|
|
131
|
-
result: 'CLOSED'
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
case 10:
|
|
135
|
-
case "end":
|
|
136
|
-
return _context3.stop();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}, _callee3);
|
|
140
|
-
})));
|
|
141
|
-
it('should accept registerRealtime parameter', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
|
|
142
|
-
var promise, result;
|
|
143
|
-
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
144
|
-
while (1) {
|
|
145
|
-
switch (_context4.prev = _context4.next) {
|
|
146
|
-
case 0:
|
|
147
|
-
promise = openWindow({
|
|
148
|
-
url: 'http://cozy.tools',
|
|
149
|
-
title: 'SOME_TITLE',
|
|
150
|
-
width: '20',
|
|
151
|
-
height: '30',
|
|
152
|
-
handleUrlChange: handleUrlChangeWrapper,
|
|
153
62
|
registerRealtime: registerRealtime
|
|
154
63
|
});
|
|
155
64
|
expect(isPending(promise)).toBeTruthy();
|
|
156
65
|
expect(registerRealtime).toHaveBeenCalledTimes(1);
|
|
157
66
|
expect(unregisterRealtime).not.toHaveBeenCalled();
|
|
158
|
-
mockPopup.emit('loadstart', 'HELLO');
|
|
159
|
-
expect(handleUrlChange).not.toHaveBeenCalled();
|
|
160
67
|
mockPopup.close();
|
|
161
|
-
|
|
68
|
+
_context2.next = 7;
|
|
162
69
|
return promise;
|
|
163
70
|
|
|
164
|
-
case
|
|
165
|
-
result =
|
|
71
|
+
case 7:
|
|
72
|
+
result = _context2.sent;
|
|
166
73
|
expect(result).toStrictEqual({
|
|
167
74
|
result: 'CLOSED'
|
|
168
75
|
});
|
|
169
76
|
expect(unregisterRealtime).toHaveBeenCalledTimes(1);
|
|
170
77
|
|
|
171
|
-
case
|
|
78
|
+
case 10:
|
|
172
79
|
case "end":
|
|
173
|
-
return
|
|
80
|
+
return _context2.stop();
|
|
174
81
|
}
|
|
175
82
|
}
|
|
176
|
-
},
|
|
83
|
+
}, _callee2);
|
|
177
84
|
})));
|
|
178
85
|
});
|
|
179
86
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-harvest-lib",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.5.0",
|
|
4
4
|
"description": "Provides logic, modules and components for Cozy's harvest applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"cozy-intent": "^2.30.0",
|
|
72
72
|
"cozy-interapp": "^0.16.0",
|
|
73
73
|
"cozy-keys-lib": "^6.1.1",
|
|
74
|
-
"cozy-realtime": "^5.
|
|
74
|
+
"cozy-realtime": "^5.7.0",
|
|
75
75
|
"cozy-tsconfig": "^1.8.1",
|
|
76
76
|
"cozy-ui": "^115.0.2",
|
|
77
|
-
"cozy-viewer": "^23.
|
|
77
|
+
"cozy-viewer": "^23.2.0",
|
|
78
78
|
"enzyme": "3.11.0",
|
|
79
79
|
"enzyme-adapter-react-16": "1.15.6",
|
|
80
80
|
"form-data": "4.0.0",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"react-router-dom": ">=6.14.2"
|
|
111
111
|
},
|
|
112
112
|
"sideEffects": false,
|
|
113
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "110b201fa4ddf5c89dfa634b88b6e495b410e90f"
|
|
114
114
|
}
|
|
@@ -17,41 +17,6 @@ export const OAUTH_SERVICE_CLOSED = 'CLOSED'
|
|
|
17
17
|
export const OAUTH_SERVICE_ERROR = 'ERROR'
|
|
18
18
|
export const OAUTH_SERVICE_OK = 'OK'
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Monitor URL changes for mobile apps and in app browsers
|
|
22
|
-
* @param {import('cozy-client/types/types').KonnectorsDoctype} konnector
|
|
23
|
-
*
|
|
24
|
-
* The provider redirect us to something like : oauthcallback.mycozy.cloud/?account=A&state=b
|
|
25
|
-
* So we listen to the URL change with these informations, and we try to handle it.
|
|
26
|
-
* It works well on iOS or when we are logged on the same device on the web, but it will
|
|
27
|
-
* fail on Android if we're ne logged in the browser. Why ?
|
|
28
|
-
* Our oauthcallback.mycozy.cloud/?account=A&state=b checks if we're logged, if not the server
|
|
29
|
-
* sends an http redirect.
|
|
30
|
-
* On Android, the 'loadstart' event is not dispatched by the browser when it get a redirect.
|
|
31
|
-
* The inAppBrowser follows the URL and arrive on :
|
|
32
|
-
* https://my.mycozy.cloud/auth?redirect=https://home.cozy.cloud/?account=a&state=b
|
|
33
|
-
* So if we don't have account & state searchParams we have to check if we've a redirect searchParams
|
|
34
|
-
* init it and search if we have inside this url an account and state params
|
|
35
|
-
*/
|
|
36
|
-
function handleUrlChange(konnector) {
|
|
37
|
-
return resolve => {
|
|
38
|
-
return event => {
|
|
39
|
-
const { url } = event
|
|
40
|
-
const oAuthData = extractOAuthDataFromUrl(url)
|
|
41
|
-
if (oAuthData) {
|
|
42
|
-
return handleOAuthData(oAuthData, konnector, resolve)
|
|
43
|
-
}
|
|
44
|
-
const redirect = url.searchParams.get('redirect')
|
|
45
|
-
if (redirect) {
|
|
46
|
-
const redirectOAuthData = extractOAuthDataFromUrl(new URL(redirect))
|
|
47
|
-
if (redirectOAuthData) {
|
|
48
|
-
return handleOAuthData(redirectOAuthData, konnector, resolve)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
20
|
/**
|
|
56
21
|
* Handles OAuth data. OAuth data may be provided by different way:
|
|
57
22
|
* * realtime message from web apps (see handleMessage)
|
|
@@ -89,26 +54,6 @@ function handleOAuthData(data, konnector, resolve) {
|
|
|
89
54
|
* @property {String} [finalLocation] - final query string added to the redirected url
|
|
90
55
|
*/
|
|
91
56
|
|
|
92
|
-
/**
|
|
93
|
-
* Extract OAuthData from url search params if any
|
|
94
|
-
*
|
|
95
|
-
* @param {URL} url
|
|
96
|
-
* @returns {OAuthData|null} OAuthData
|
|
97
|
-
*/
|
|
98
|
-
function extractOAuthDataFromUrl(url) {
|
|
99
|
-
const account = url.searchParams.get('account')
|
|
100
|
-
const state = url.searchParams.get('state')
|
|
101
|
-
const error = url.searchParams.get('error')
|
|
102
|
-
if (account && state) {
|
|
103
|
-
const oAuthData = { key: account, oAuthStateKey: state }
|
|
104
|
-
if (error) {
|
|
105
|
-
oAuthData.error = error
|
|
106
|
-
}
|
|
107
|
-
return oAuthData
|
|
108
|
-
}
|
|
109
|
-
return null
|
|
110
|
-
}
|
|
111
|
-
|
|
112
57
|
/**
|
|
113
58
|
* @typedef OAuthServiceResult
|
|
114
59
|
* @property {"OK"|"CLOSED"|"ERROR"} result
|
|
@@ -331,7 +276,6 @@ export function openOAuthWindow({
|
|
|
331
276
|
title: title || '',
|
|
332
277
|
width: OAUTH_POPUP_WIDTH,
|
|
333
278
|
height: OAUTH_POPUP_HEIGHT,
|
|
334
|
-
handleUrlChange: handleUrlChange(konnector),
|
|
335
279
|
registerRealtime: registerRealtime({ konnector, client })
|
|
336
280
|
})
|
|
337
281
|
}
|
|
@@ -3,7 +3,7 @@ import util from 'util'
|
|
|
3
3
|
import { openOAuthWindow, OAUTH_SERVICE_OK } from 'components/OAuthService'
|
|
4
4
|
import MicroEE from 'microee'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { isFlagshipApp } from 'cozy-device-helper'
|
|
7
7
|
|
|
8
8
|
import { OAUTH_REALTIME_CHANNEL, terminateOAuth } from '../helpers/oauth'
|
|
9
9
|
import PopupMock from '../helpers/windowWrapperMocks'
|
|
@@ -60,7 +60,6 @@ const client = {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
jest.mock('cozy-device-helper', () => ({
|
|
63
|
-
isMobileApp: jest.fn(),
|
|
64
63
|
isFlagshipApp: jest.fn()
|
|
65
64
|
}))
|
|
66
65
|
|
|
@@ -135,71 +134,8 @@ describe('OAuthService', () => {
|
|
|
135
134
|
})
|
|
136
135
|
})
|
|
137
136
|
|
|
138
|
-
describe('with intentsApi', () => {
|
|
139
|
-
beforeEach(() => {
|
|
140
|
-
isMobileApp.mockReturnValue(true)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('should render', async () => {
|
|
144
|
-
let closePromise = new Promise(() => {})
|
|
145
|
-
const intentsApi = {
|
|
146
|
-
fetchSessionCode: jest.fn().mockResolvedValue('somesessioncode'),
|
|
147
|
-
showInAppBrowser: jest.fn().mockImplementation(() => {
|
|
148
|
-
return closePromise
|
|
149
|
-
}),
|
|
150
|
-
closeInAppBrowser: jest.fn().mockResolvedValue()
|
|
151
|
-
}
|
|
152
|
-
const promise = openOAuthWindow({
|
|
153
|
-
client,
|
|
154
|
-
konnector: { slug: 'ameli' },
|
|
155
|
-
redirectSlug: 'home',
|
|
156
|
-
extraParams: { extraParam1: 'extraParamValue' },
|
|
157
|
-
title: 'popup title',
|
|
158
|
-
account: { _id: 'someaccountid' },
|
|
159
|
-
intentsApi
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
expect(isPending(promise)).toBe(true)
|
|
163
|
-
expect(terminateOAuth).not.toHaveBeenCalled()
|
|
164
|
-
|
|
165
|
-
await sleep(100) // lets time to show inAppBrowser before realtime event occurs
|
|
166
|
-
|
|
167
|
-
mockCozyRealtime.emitRealtimeEvent(
|
|
168
|
-
'notified',
|
|
169
|
-
'io.cozy.accounts',
|
|
170
|
-
OAUTH_REALTIME_CHANNEL,
|
|
171
|
-
{
|
|
172
|
-
_id: 'oauth-popup',
|
|
173
|
-
data: {
|
|
174
|
-
error: null,
|
|
175
|
-
finalLocation: 'connection_id=194&state=someuuid',
|
|
176
|
-
key: null,
|
|
177
|
-
oAuthStateKey: 'someuuid'
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
)
|
|
181
|
-
const result = await promise
|
|
182
|
-
expect(result).toStrictEqual({
|
|
183
|
-
result: OAUTH_SERVICE_OK,
|
|
184
|
-
key: null,
|
|
185
|
-
data: {
|
|
186
|
-
error: null,
|
|
187
|
-
finalLocation: 'connection_id=194&state=someuuid',
|
|
188
|
-
key: null,
|
|
189
|
-
oAuthStateKey: 'someuuid'
|
|
190
|
-
}
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
expect(terminateOAuth).toHaveBeenCalled()
|
|
194
|
-
expect(intentsApi.fetchSessionCode).toHaveBeenCalledTimes(1)
|
|
195
|
-
expect(intentsApi.showInAppBrowser).toHaveBeenCalled() // TODO test url mock nonce
|
|
196
|
-
expect(intentsApi.closeInAppBrowser).toHaveBeenCalled()
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
137
|
describe('on web', () => {
|
|
201
138
|
beforeEach(() => {
|
|
202
|
-
isMobileApp.mockReturnValue(false)
|
|
203
139
|
isFlagshipApp.mockReturnValue(false)
|
|
204
140
|
})
|
|
205
141
|
|
|
@@ -251,7 +187,3 @@ describe('OAuthService', () => {
|
|
|
251
187
|
const isPending = promise => {
|
|
252
188
|
return util.inspect(promise).includes('pending')
|
|
253
189
|
}
|
|
254
|
-
|
|
255
|
-
function sleep(ms) {
|
|
256
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
257
|
-
}
|
package/src/components/Popup.jsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import { PureComponent } from 'react'
|
|
3
3
|
|
|
4
|
-
import { isMobileApp } from 'cozy-device-helper'
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Customized function to get dimensions and position for a centered
|
|
8
6
|
* popup window
|
|
@@ -61,23 +59,6 @@ export class Popup extends PureComponent {
|
|
|
61
59
|
this.killPopup()
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
addListeners(popup) {
|
|
65
|
-
// rest of instructions only on mobile app
|
|
66
|
-
if (!isMobileApp()) return
|
|
67
|
-
popup.addEventListener('loadstart', this.handleUrlChange)
|
|
68
|
-
popup.addEventListener('exit', this.handleClose)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
removeListeners(popup) {
|
|
72
|
-
// rest of instructions only if popup is still opened
|
|
73
|
-
if (popup.closed) return
|
|
74
|
-
|
|
75
|
-
// rest of instructions only on mobile app
|
|
76
|
-
if (!isMobileApp()) return
|
|
77
|
-
popup.removeEventListener('loadstart', this.handleUrlChange)
|
|
78
|
-
popup.removeEventListener('exit', this.handleClose)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
62
|
handleClose(popup) {
|
|
82
63
|
this.killPopup()
|
|
83
64
|
|
|
@@ -111,14 +92,12 @@ export class Popup extends PureComponent {
|
|
|
111
92
|
popup.focus()
|
|
112
93
|
}
|
|
113
94
|
|
|
114
|
-
this.addListeners(popup)
|
|
115
95
|
this.startMonitoringClosing(popup)
|
|
116
96
|
this.setState({ popup })
|
|
117
97
|
}
|
|
118
98
|
|
|
119
99
|
killPopup() {
|
|
120
100
|
const { popup } = this.state
|
|
121
|
-
this.removeListeners(popup)
|
|
122
101
|
this.stopMonitoringClosing()
|
|
123
102
|
if (!popup.closed) popup.close()
|
|
124
103
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { isMobileApp } from 'cozy-device-helper'
|
|
3
|
-
|
|
4
2
|
import { popupCenter } from './Popup'
|
|
5
3
|
import { windowOpen } from '../helpers/windowWrapper'
|
|
6
4
|
|
|
@@ -17,18 +15,10 @@ import { windowOpen } from '../helpers/windowWrapper'
|
|
|
17
15
|
* @param {String} options.title - Title of the popup
|
|
18
16
|
* @param {Number} options.width - Width of the popup
|
|
19
17
|
* @param {Number} options.height - Height of the popup
|
|
20
|
-
* @param {Function} options.handleUrlChange - Function to call when popup url changes
|
|
21
18
|
* @param {Function} options.registerRealtime - Function to call to register realtime events
|
|
22
19
|
* @returns {Promise<OpenWindowResult>}
|
|
23
20
|
*/
|
|
24
|
-
export function openWindow({
|
|
25
|
-
url,
|
|
26
|
-
title,
|
|
27
|
-
width,
|
|
28
|
-
height,
|
|
29
|
-
handleUrlChange,
|
|
30
|
-
registerRealtime
|
|
31
|
-
}) {
|
|
21
|
+
export function openWindow({ url, title, width, height, registerRealtime }) {
|
|
32
22
|
const { w, h, top, left } = popupCenter(width, height)
|
|
33
23
|
const popup = windowOpen(
|
|
34
24
|
url,
|
|
@@ -51,9 +41,6 @@ export function openWindow({
|
|
|
51
41
|
let checkClosedInterval
|
|
52
42
|
let unregisterRealtime
|
|
53
43
|
const cleanAndResolve = data => {
|
|
54
|
-
if (handleUrlChange) {
|
|
55
|
-
removeListeners(popup, handleUrlChange)
|
|
56
|
-
}
|
|
57
44
|
stopMonitoringClosing(checkClosedInterval)
|
|
58
45
|
if (registerRealtime) {
|
|
59
46
|
unregisterRealtime()
|
|
@@ -61,9 +48,6 @@ export function openWindow({
|
|
|
61
48
|
if (!popup.closed) popup.close()
|
|
62
49
|
resolve(data)
|
|
63
50
|
}
|
|
64
|
-
if (handleUrlChange) {
|
|
65
|
-
addListeners(popup, cleanAndResolve, handleUrlChange)
|
|
66
|
-
}
|
|
67
51
|
if (registerRealtime) {
|
|
68
52
|
unregisterRealtime = registerRealtime(cleanAndResolve)
|
|
69
53
|
}
|
|
@@ -107,47 +91,3 @@ function startMonitoringClosing(popup, resolve) {
|
|
|
107
91
|
function stopMonitoringClosing(checkClosedInterval) {
|
|
108
92
|
clearInterval(checkClosedInterval)
|
|
109
93
|
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Removes all listeners
|
|
113
|
-
*
|
|
114
|
-
* @param {Object} popup - Current popup
|
|
115
|
-
* @param {Function} handleUrlChange - handleUrlChange function not to listen anymore
|
|
116
|
-
* @returns <void>
|
|
117
|
-
*/
|
|
118
|
-
function removeListeners(popup, handleUrlChange) {
|
|
119
|
-
// rest of instructions only if popup is still opened
|
|
120
|
-
if (popup.closed) return
|
|
121
|
-
|
|
122
|
-
// rest of instructions only on mobile app
|
|
123
|
-
if (!isMobileApp()) return
|
|
124
|
-
popup.removeEventListener('loadstart', handleUrlChange)
|
|
125
|
-
popup.removeEventListener('exit', handleClose)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Add listeners
|
|
130
|
-
*
|
|
131
|
-
* @param {Object} popup
|
|
132
|
-
* @param {Function} resolve
|
|
133
|
-
* @param {Function} handleUrlChange
|
|
134
|
-
* @returns <void>
|
|
135
|
-
*/
|
|
136
|
-
function addListeners(popup, resolve, handleUrlChange) {
|
|
137
|
-
// rest of instructions only on mobile app
|
|
138
|
-
if (!isMobileApp()) return
|
|
139
|
-
popup.addEventListener('loadstart', handleUrlChange(resolve))
|
|
140
|
-
popup.addEventListener('exit', handleClose(resolve))
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Function called when the popup is closed
|
|
145
|
-
*
|
|
146
|
-
* @param {Function} resolve
|
|
147
|
-
* @returns {Function}
|
|
148
|
-
*/
|
|
149
|
-
function handleClose(resolve) {
|
|
150
|
-
return popup => {
|
|
151
|
-
resolve(popup)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
@@ -2,8 +2,6 @@ import util from 'util'
|
|
|
2
2
|
|
|
3
3
|
import { openWindow } from 'components/PopupService'
|
|
4
4
|
|
|
5
|
-
import { isMobileApp } from 'cozy-device-helper'
|
|
6
|
-
|
|
7
5
|
import PopupMock from '../helpers/windowWrapperMocks'
|
|
8
6
|
|
|
9
7
|
const mockPopup = new PopupMock()
|
|
@@ -13,14 +11,8 @@ jest.mock('../helpers/windowWrapper', () => ({
|
|
|
13
11
|
})
|
|
14
12
|
}))
|
|
15
13
|
|
|
16
|
-
jest.mock('cozy-device-helper', () => ({
|
|
17
|
-
isMobileApp: jest.fn()
|
|
18
|
-
}))
|
|
19
|
-
|
|
20
14
|
describe('PopupService', () => {
|
|
21
15
|
describe('openWindow', () => {
|
|
22
|
-
const handleUrlChange = jest.fn()
|
|
23
|
-
const handleUrlChangeWrapper = jest.fn().mockReturnValue(handleUrlChange)
|
|
24
16
|
const unregisterRealtime = jest.fn()
|
|
25
17
|
const registerRealtime = jest.fn().mockReturnValue(unregisterRealtime)
|
|
26
18
|
|
|
@@ -32,64 +24,7 @@ describe('PopupService', () => {
|
|
|
32
24
|
jest.clearAllMocks()
|
|
33
25
|
})
|
|
34
26
|
|
|
35
|
-
describe('on Cordova', () => {
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
isMobileApp.mockReturnValue(true)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should render', async () => {
|
|
41
|
-
const promise = openWindow({
|
|
42
|
-
url: 'http://cozy.tools',
|
|
43
|
-
title: 'SOME_TITLE',
|
|
44
|
-
width: '20',
|
|
45
|
-
height: '30',
|
|
46
|
-
handleUrlChange: handleUrlChangeWrapper
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
expect(mockPopup.focus).toHaveBeenCalled()
|
|
50
|
-
expect(mockPopup.addEventListener).toHaveBeenCalledWith(
|
|
51
|
-
'loadstart',
|
|
52
|
-
expect.anything()
|
|
53
|
-
)
|
|
54
|
-
expect(mockPopup.addEventListener).toHaveBeenCalledWith(
|
|
55
|
-
'exit',
|
|
56
|
-
expect.anything()
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
expect(isPending(promise)).toBeTruthy()
|
|
60
|
-
|
|
61
|
-
mockPopup.emit('exit', { POPUP: 'data' })
|
|
62
|
-
const result = await promise
|
|
63
|
-
|
|
64
|
-
expect(result).toStrictEqual({ POPUP: 'data' })
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should call handUrlChange', async () => {
|
|
68
|
-
const promise = openWindow({
|
|
69
|
-
url: 'http://cozy.tools',
|
|
70
|
-
title: 'SOME_TITLE',
|
|
71
|
-
width: '20',
|
|
72
|
-
height: '30',
|
|
73
|
-
handleUrlChange: handleUrlChangeWrapper
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
expect(isPending(promise)).toBeTruthy()
|
|
77
|
-
|
|
78
|
-
mockPopup.emit('loadstart', 'HELLO')
|
|
79
|
-
expect(handleUrlChangeWrapper).toHaveBeenCalled()
|
|
80
|
-
expect(handleUrlChange).toHaveBeenCalledWith('HELLO')
|
|
81
|
-
|
|
82
|
-
mockPopup.emit('exit', { POPUP: 'data' })
|
|
83
|
-
const result = await promise
|
|
84
|
-
expect(result).toStrictEqual({ POPUP: 'data' })
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
27
|
describe('on web', () => {
|
|
89
|
-
beforeEach(() => {
|
|
90
|
-
isMobileApp.mockReturnValue(false)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
28
|
it('should render', async () => {
|
|
94
29
|
const promise = openWindow(
|
|
95
30
|
'http://cozy.tools',
|
|
@@ -98,14 +33,6 @@ describe('PopupService', () => {
|
|
|
98
33
|
)
|
|
99
34
|
|
|
100
35
|
expect(mockPopup.focus).toHaveBeenCalled()
|
|
101
|
-
expect(mockPopup.addEventListener).not.toHaveBeenCalledWith(
|
|
102
|
-
'loadstart',
|
|
103
|
-
expect.anything()
|
|
104
|
-
)
|
|
105
|
-
expect(mockPopup.addEventListener).not.toHaveBeenCalledWith(
|
|
106
|
-
'exit',
|
|
107
|
-
expect.anything()
|
|
108
|
-
)
|
|
109
36
|
|
|
110
37
|
expect(isPending(promise)).toBeTruthy()
|
|
111
38
|
|
|
@@ -120,7 +47,6 @@ describe('PopupService', () => {
|
|
|
120
47
|
title: 'SOME_TITLE',
|
|
121
48
|
width: '20',
|
|
122
49
|
height: '30',
|
|
123
|
-
handleUrlChange: handleUrlChangeWrapper,
|
|
124
50
|
registerRealtime
|
|
125
51
|
})
|
|
126
52
|
|
|
@@ -129,9 +55,6 @@ describe('PopupService', () => {
|
|
|
129
55
|
expect(registerRealtime).toHaveBeenCalledTimes(1)
|
|
130
56
|
expect(unregisterRealtime).not.toHaveBeenCalled()
|
|
131
57
|
|
|
132
|
-
mockPopup.emit('loadstart', 'HELLO')
|
|
133
|
-
expect(handleUrlChange).not.toHaveBeenCalled()
|
|
134
|
-
|
|
135
58
|
mockPopup.close()
|
|
136
59
|
const result = await promise
|
|
137
60
|
expect(result).toStrictEqual({ result: 'CLOSED' })
|