UNPKG

filestack-js

Version:

Official JavaScript library for Filestack

748 lines (747 loc) 82.5 kB
/* * Copyright (c) 2019 by Filestack. * Some rights reserved. * * Licensed 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 { __assign, __awaiter, __generator } from "tslib"; import { config } from './../../config'; import { CloudClient, PICKER_KEY } from './cloud'; import * as utils from './../utils/index.node'; import nock from 'nock'; import { Store, STORE_TYPE } from '../utils/store'; var testApiKey = 'API_KEY'; var testTokSession = 'TOK_SESSION'; var testTokArchiveId = 'ARCHIVE_ID'; var testCloudToken = 'testCloudToken'; var testSecurity = { policy: 'examplePolicy', signature: 'exampleSignature', }; var sessionURls = config.urls; var testSession = { apikey: testApiKey, urls: sessionURls, }; var scope = nock(sessionURls.cloudApiUrl); scope.defaultReplyHeaders({ 'access-control-allow-origin': function (req) { var _a; return (_a = req.getHeader('origin')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'access-control-allow-methods': function (req) { var _a; return (_a = req.getHeader('access-control-request-method')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'access-control-allow-headers': function (req) { var _a; return (_a = req.getHeader('access-control-request-headers')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'content-type': 'application/json', }); var mockTokInit = jest .fn() .mockName('tokInit') .mockReturnValue('init'); var mockTokStart = jest .fn() .mockName('tokStart') .mockReturnValue('start'); var mockTokStop = jest .fn() .mockName('tokStop') .mockReturnValue('stop'); var mockMetadata = jest .fn() .mockName('metadata') .mockReturnValue('metadata'); var mockList = jest .fn() .mockName('list') .mockImplementation(function (data) { if (data && data.clouds.token) { return { token: testCloudToken }; } return data; }); var mockLogout = jest .fn() .mockName('logout') .mockImplementation(function (url, params) { if (params.clouds && params.clouds.token) { return { token: testCloudToken }; } return params; }); var mockStore = jest .fn() .mockName('store') .mockImplementation(function (params) { if (params && params.clouds && params.clouds.token) { return JSON.stringify({ token: testCloudToken }); } return JSON.stringify(params); }); describe('cloud', function () { beforeEach(function () { scope .persist() .options(/.*/) .reply(204, '', { 'access-control-allow-headers': 'filestack-source,filestack-trace-id,filestack-trace-span', 'access-control-allow-methods': '*', 'access-control-allow-origin': '*', }); scope.post('/auth/logout').reply(200, mockLogout); scope.post('/folder/list').reply(200, function (_, data) { return mockList(data); }); scope.post('/store/').reply(200, function (_, data) { return mockStore(data); }); scope.post('/metadata').reply(200, mockMetadata); scope.post(/\/recording\/(audio|video)\/init/).reply(200, mockTokInit); scope.post(/\/recording\/(audio|video)\/start/).reply(200, mockTokStart); scope.post(/\/recording\/(audio|video)\/stop/).reply(200, mockTokStop); }); afterEach(function () { nock.cleanAll(); jest.clearAllMocks(); localStorage.clear(); }); describe('cancelToken', function () { var testDomain = 'http://delay.filestack.test'; var scopeD; beforeEach(function () { scopeD = nock(testDomain); scopeD .post('/store/') .delay(4000) .reply(200); scopeD .post('/folder/list') .delay(4000) .reply(200); scopeD .persist() .options(/.*/) .reply(204, '', { 'access-control-allow-headers': 'filestack-source,filestack-trace-id,filestack-trace-span', 'access-control-allow-methods': '*', 'access-control-allow-origin': '*', }); }); afterEach(function () { nock.cleanAll(); jest.clearAllMocks(); localStorage.clear(); }); it('Should cancel store request', function (done) { var sessionClone = JSON.parse(JSON.stringify(testSession)); sessionClone.urls.cloudApiUrl = testDomain; var token = {}; new CloudClient(sessionClone) .store('google', 'test', { filename: '1', location: 'gcs' }, {}, token) .then(function () { done('Request not canceled'); }) .catch(function (err) { expect(err).toEqual(expect.any(Error)); done(); }); setTimeout(function () { // @ts-ignore token.cancel(); }, 500); }); it('Should cancel list request', function (done) { var sessionClone = JSON.parse(JSON.stringify(testSession)); sessionClone.urls.cloudApiUrl = testDomain; var token = {}; new CloudClient(sessionClone) .list('google', token) .then(function () { done('Request not canceled'); }) .catch(function (err) { expect(err).toEqual(expect.any(Error)); done(); }); setTimeout(function () { // @ts-ignore token.cancel(); }, 500); }); }); describe('facebook inapp browser', function () { it('should set token to sessionStore when inapp browser is detected', function () { return __awaiter(void 0, void 0, void 0, function () { var client, token, store; return __generator(this, function (_a) { jest.spyOn(utils, 'isFacebook').mockReturnValue(true); client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); token = 'test'; client.token = token; store = new Store(); expect(store.getItem(PICKER_KEY, STORE_TYPE.SESSION)).toEqual(token); store.setItem(PICKER_KEY, undefined, STORE_TYPE.SESSION); return [2 /*return*/]; }); }); }); it('should get token from sessionStore when inapp browser is detected', function () { return __awaiter(void 0, void 0, void 0, function () { var client, store, token; return __generator(this, function (_a) { jest.spyOn(utils, 'isFacebook').mockReturnValue(true); client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); store = new Store(); token = 'test'; store.setItem(PICKER_KEY, token, STORE_TYPE.SESSION); expect(client.token).toEqual(token); store.setItem(PICKER_KEY, undefined, STORE_TYPE.SESSION); return [2 /*return*/]; }); }); }); it('should send appurl in list action', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, }); return [2 /*return*/]; } }); }); }); it('should send pass mimetypes to backend as string', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(__assign({}, clouds), {}, 'image/*')]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, accept: ['image/*'], }); return [2 /*return*/]; } }); }); }); it('should send pass mimetypes to backend as array', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(__assign({}, clouds), {}, ['image/*'])]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, accept: ['image/*'], }); return [2 /*return*/]; } }); }); }); }); it('should not send app url if urlsearch params is undefined', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, before, client, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; before = window.URLSearchParams; window.URLSearchParams = undefined; client = new CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: clouds, token: null, }); window.URLSearchParams = before; return [2 /*return*/]; } }); }); }); describe('list', function () { it('should make correct list request', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; return [4 /*yield*/, new CloudClient(testSession).list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: clouds, }); return [2 /*return*/]; } }); }); }); it('should make correct list request with session cache', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; localStorage.setItem(PICKER_KEY, testCloudToken); return [4 /*yield*/, new CloudClient(testSession, { sessionCache: true, }).list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', token: testCloudToken, clouds: clouds, }); return [2 /*return*/]; } }); }); }); it('should set token on api token response', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { token: true }; return [4 /*yield*/, new CloudClient(testSession).list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ token: testCloudToken }); return [2 /*return*/]; } }); }); }); it('should cache session token to local storage', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { token: true }; return [4 /*yield*/, new CloudClient(testSession, { sessionCache: true }).list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(localStorage.setItem).toHaveBeenCalledWith(PICKER_KEY, testCloudToken); expect(res).toEqual({ token: testCloudToken }); return [2 /*return*/]; } }); }); }); it('should make correct list request with security', function () { return __awaiter(void 0, void 0, void 0, function () { var clouds, res; return __generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; return [4 /*yield*/, new CloudClient(__assign(__assign({}, testSession), testSecurity)).list(__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual(__assign({ apikey: testApiKey, flow: 'web', clouds: clouds }, testSecurity)); return [2 /*return*/]; } }); }); }); }); describe('store', function () { it('should make correct basic request', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).store('google', 'test', { filename: '1' })]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { google: { path: 'test', store: { filename: '1', location: 's3', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should respect store location param', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).store('google', 'test', { filename: '1', location: 'gcs' })]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { google: { path: 'test', store: { filename: '1', location: 'gcs', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should respect store upload tags', function () { return __awaiter(void 0, void 0, void 0, function () { var uploadTags, res; return __generator(this, function (_a) { switch (_a.label) { case 0: uploadTags = { testTag: 'testTag' }; return [4 /*yield*/, new CloudClient(testSession).store('google', 'test', {}, null, null, uploadTags)]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', upload_tags: uploadTags, clouds: { google: { path: 'test', store: { location: 's3', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should make correct basic with security', function () { return __awaiter(void 0, void 0, void 0, function () { var res, excepted; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(__assign(__assign({}, testSession), testSecurity)).store('token', 'test', { filename: '1' })]; case 1: res = _a.sent(); excepted = __assign(__assign({}, testSecurity), { apikey: testApiKey, flow: 'web', clouds: { token: { path: 'test', store: { filename: '1', location: 's3', }, }, } }); expect(mockStore).toHaveBeenCalledWith(excepted); expect(res).toEqual(testCloudToken); return [2 /*return*/]; } }); }); }); it('should handle custom source', function () { return __awaiter(void 0, void 0, void 0, function () { var customSource, res; return __generator(this, function (_a) { switch (_a.label) { case 0: customSource = { customSourcePath: 'cs_path', customSourceContainer: 'cs_container', }; return [4 /*yield*/, new CloudClient(testSession).store('customsource', 'test', { filename: '1' }, customSource)]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { customsource: __assign(__assign({}, customSource), { path: 'test', store: { filename: '1', location: 's3', } }), }, }); return [2 /*return*/]; } }); }); }); }); describe('logout', function () { it('should make correct request to logout', function () { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new CloudClient(testSession).logout()]; case 1: _a.apply(void 0, [_b.sent()]).toEqual({ apikey: 'API_KEY', flow: 'web' }); return [2 /*return*/]; } }); }); }); it('should make correct request to logout with provided cloud', function () { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new CloudClient(testSession).logout('google')]; case 1: _a.apply(void 0, [_b.sent()]).toEqual({ apikey: 'API_KEY', flow: 'web', clouds: { google: {} } }); return [2 /*return*/]; } }); }); }); it('should make correct request to logout and return correct response when cloud name is returned', function () { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new CloudClient(testSession).logout('token')]; case 1: _a.apply(void 0, [_b.sent()]).toEqual('testCloudToken'); return [2 /*return*/]; } }); }); }); it('should make correct request to logout and clean session cache ', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: localStorage.setItem(PICKER_KEY, testCloudToken); return [4 /*yield*/, new CloudClient(testSession, { sessionCache: true }).logout()]; case 1: res = _a.sent(); expect(localStorage.removeItem).toHaveBeenCalledWith(PICKER_KEY); expect(res).toEqual({ apikey: 'API_KEY', flow: 'web', token: testCloudToken }); return [2 /*return*/]; } }); }); }); }); describe('metadata', function () { it('should make correct request', function () { return __awaiter(void 0, void 0, void 0, function () { var testUrl, res; return __generator(this, function (_a) { switch (_a.label) { case 0: testUrl = 'http://test.com'; return [4 /*yield*/, new CloudClient(testSession).metadata(testUrl)]; case 1: res = _a.sent(); expect(mockMetadata).toHaveBeenCalledWith(expect.any(String), { apikey: testApiKey, url: testUrl, }); expect(res).toEqual('metadata'); return [2 /*return*/]; } }); }); }); it('should make correct request with security', function () { return __awaiter(void 0, void 0, void 0, function () { var testUrl, res; return __generator(this, function (_a) { switch (_a.label) { case 0: testUrl = 'http://test.com'; return [4 /*yield*/, new CloudClient(__assign(__assign({}, testSession), testSecurity)).metadata(testUrl)]; case 1: res = _a.sent(); expect(mockMetadata).toHaveBeenCalledWith(expect.any(String), __assign({ apikey: testApiKey, url: testUrl }, testSecurity)); expect(res).toEqual('metadata'); return [2 /*return*/]; } }); }); }); }); describe('OpenTok', function () { describe('tokInit', function () { it('should make correct request to api (audio)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokInit('audio')]; case 1: res = _a.sent(); expect(mockTokInit).toHaveBeenCalledWith(expect.any(String), ''); expect(res).toEqual('init'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokInit('audio')]; case 1: res = _a.sent(); expect(mockTokInit).toHaveBeenCalledWith(expect.any(String), ''); expect(res).toEqual('init'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { expect(function () { new CloudClient(testSession) .tokInit('videoa') .then(function () { console.log('init'); }) .catch(function () { console.log('err'); }); }).toThrowError(); return [2 /*return*/]; }); }); }); }); describe('tokStart', function () { it('should make correct request to api (audio)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokStart('audio', 'key', testTokSession)]; case 1: res = _a.sent(); expect(mockTokStart).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession }); expect(res).toEqual('start'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokStart('video', 'key', testTokSession)]; case 1: res = _a.sent(); expect(mockTokStart).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession }); expect(res).toEqual('start'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { expect(function () { return new CloudClient(testSession).tokStart('videoa', 'key', testTokSession); }).toThrowError(); }); }); describe('tokStop', function () { it('should make correct request to api (audio)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokStop('audio', 'key', testTokSession, testTokArchiveId)]; case 1: res = _a.sent(); expect(mockTokStop).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession, archive_id: testTokArchiveId, }); expect(res).toEqual('stop'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return __awaiter(void 0, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CloudClient(testSession).tokStop('video', 'key', testTokSession, testTokArchiveId)]; case 1: res = _a.sent(); expect(mockTokStop).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession, archive_id: testTokArchiveId, }); expect(res).toEqual('stop'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { expect(function () { return new CloudClient(testSession).tokStop('videoa', 'key', testTokSession, testTokArchiveId); }).toThrowError(); }); }); }); }); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvYXBpL2Nsb3VkLnNwZWMuYnJvd3Nlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7O0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2xELE9BQU8sS0FBSyxLQUFLLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbkQsSUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDO0FBQzdCLElBQU0sY0FBYyxHQUFHLGFBQWEsQ0FBQztBQUNyQyxJQUFNLGdCQUFnQixHQUFHLFlBQVksQ0FBQztBQUV0QyxJQUFNLGNBQWMsR0FBRyxnQkFBZ0IsQ0FBQztBQUV4QyxJQUFNLFlBQVksR0FBRztJQUNuQixNQUFNLEVBQUUsZUFBZTtJQUN2QixTQUFTLEVBQUUsa0JBQWtCO0NBQzlCLENBQUM7QUFFRixJQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2hDLElBQU0sV0FBVyxHQUFHO0lBQ2xCLE1BQU0sRUFBRSxVQUFVO0lBQ2xCLElBQUksRUFBRSxXQUFXO0NBQ2xCLENBQUM7QUFFRixJQUFJLEtBQUssR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBRTFDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQztJQUN4Qiw2QkFBNkIsRUFBRSxVQUFVLEdBQUcsWUFBSSxPQUFPLE1BQUEsR0FBRyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsMENBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdGLDhCQUE4QixFQUFFLFVBQVUsR0FBRyxZQUFJLE9BQU8sTUFBQSxHQUFHLENBQUMsU0FBUyxDQUFDLCtC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