var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import React from 'react';
import { nanoid } from 'nanoid';
import { act, renderHook, waitFor } from '@testing-library/react';
import * as request from '@reltio/mdm-sdk/src/network';
import { CustomAction, CustomScriptPlatform, RequestAction, setRequestProvider, createRequestProvider, initializeWebWorker } from '@reltio/mdm-sdk';
import { MdmModuleProvider } from '../../contexts/MdmModuleContext';
import { InterceptHandlersContext } from '../../contexts/InterceptHandlersContext';
import { SnackbarContext } from '../../contexts/SnackbarContext';
import { useCustomScripts } from './useCustomScripts';
jest.mock('@reltio/mdm-sdk', function () { return (__assign(__assign({}, jest.requireActual('@reltio/mdm-sdk')), { initializeWebWorker: jest.fn() })); });
jest.mock('nanoid', function () { return (__assign(__assign({}, jest.requireActual('nanoid')), { nanoid: jest.fn() })); });
var defaultMdmValues = {
    apiPath: '/nui/reltioAPI',
    servicesPath: 'https://tst-01.reltio.com/nui/services',
    uiPath: 'http://localhost:3000/nui/uitest1/',
    reltioPath: 'https://tst-01.reltio.com/reltio',
    tenant: { id: 'uitest1', name: 'uitest1' },
    user: { username: 'test.user' },
    metadata: { uri: 'configuration', description: 'some desc' }
};
var createConfig = function () { return [
    {
        files: ['https://reltio-ui-files.s3.amazonaws.com/custom-scripts/hideAttr.js'],
        permissions: [
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/?_]+/?($|\\?.*)',
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/?_]+/_slice?($|\\?.*)',
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/_]+/_update/?($|\\?.*)'
        ],
        processApiRequest: [
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/?_]+/?($|\\?.*)',
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/_]+/_update/?($|\\?.*)'
        ],
        processApiResponse: [
            'https://[^.]+.reltio.com/reltio/api/[^/]+/entities/[^/_]+/attributes/[^?]+\\?crosswalkValue=.+'
        ]
    }
]; };
describe('useCustomScripts', function () {
    var showSnackbarMessage = jest.fn();
    var refreshToken = jest.fn().mockResolvedValue(undefined);
    var requestProvider = createRequestProvider(refreshToken);
    var interceptors = {
        addInternalRequestInterceptor: requestProvider.addRequestInterceptor,
        addInternalResponseInterceptor: requestProvider.addResponseInterceptor,
        removeInternalRequestInterceptor: requestProvider.removeRequestInterceptor,
        removeInternalResponseInterceptor: requestProvider.removeResponseInterceptor
    };
    var setUp = function (_a) {
        var _b = _a === void 0 ? {} : _a, _c = _b.mdmValues, mdmValues = _c === void 0 ? defaultMdmValues : _c, _d = _b.config, config = _d === void 0 ? createConfig() : _d;
        var updateMdmValues;
        var Providers = function (_a) {
            var children = _a.children;
            var _b = React.useState(mdmValues), mdmValuesState = _b[0], setMdmValuesState = _b[1];
            updateMdmValues = setMdmValuesState;
            return (React.createElement(MdmModuleProvider, { values: mdmValuesState },
                React.createElement(SnackbarContext.Provider, { value: showSnackbarMessage },
                    React.createElement(InterceptHandlersContext.Provider, { value: interceptors }, children))));
        };
        return __assign(__assign({}, renderHook(useCustomScripts, { initialProps: config, wrapper: Providers })), { updateMdmValues: updateMdmValues });
    };
    var postMessage = jest.fn();
    var onmessageFromWorker;
    var fetch = jest.spyOn(window, 'fetch');
    var requestMock = jest.spyOn(request, 'rawRequest');
    var result = { ok: true, status: 200, json: function () { return Promise.resolve({ success: 'message' }); } };
    beforeAll(function () {
        Object.defineProperty(window, 'Response', { value: Object });
        setRequestProvider(fetch);
        nanoid.mockReturnValue('nanoid');
    });
    afterAll(function () {
        delete window['Response'];
    });
    beforeEach(function () {
        var worker = {
            postMessage: postMessage,
            terminate: jest.fn(),
            onmessageerror: jest.fn(),
            onerror: jest.fn(),
            onmessage: jest.fn(),
            dispatchEvent: jest.fn(),
            addEventListener: jest.fn(),
            removeEventListener: jest.fn()
        };
        initializeWebWorker.mockImplementation(function (workerUrl, process, config) {
            onmessageFromWorker = function (task) { return process({ task: task, worker: worker, config: config }); };
            return worker;
        });
        fetch.mockImplementation(function (url, _options) {
            var isTokenRequest = url.toString().includes('getTokenForWorker');
            return Promise.resolve((isTokenRequest ? { accessToken: 'some token' } : result));
        });
    });
    afterEach(function () {
        jest.clearAllMocks();
    });
    it('should intercept request', function () { return __awaiter(void 0, void 0, void 0, function () {
        var params, response, _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    setUp();
                    expect(initializeWebWorker).toHaveBeenCalledTimes(1);
                    params = {
                        name: RequestAction.API,
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk',
                        id: 'nanoid',
                        headers: { actionId: 'nanoid' }
                    };
                    response = requestProvider.request('/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk', {
                        method: 'GET'
                    });
                    act(function () {
                        onmessageFromWorker({ action: CustomAction.REQUEST, params: params });
                    });
                    expect(postMessage).toHaveBeenCalledWith({
                        action: 'apiRequest',
                        data: undefined,
                        headers: { actionId: 'nanoid' },
                        id: 'nanoid',
                        method: 'GET',
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk'
                    });
                    return [4 /*yield*/, waitFor(function () { return expect(fetch).toHaveBeenCalledTimes(2); })];
                case 1:
                    _b.sent();
                    expect(fetch).toHaveBeenCalledWith('https://tst-01.reltio.com/nui/services/getTokenForWorker', { method: 'GET' });
                    expect(fetch).toHaveBeenCalledWith('/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk', {
                        body: undefined,
                        headers: {
                            'Content-Type': 'application/json',
                            Authorization: 'Bearer some token',
                            actionId: 'nanoid'
                        },
                        noRequestProcessor: true,
                        noRetry: true,
                        method: 'GET'
                    });
                    return [4 /*yield*/, waitFor(function () {
                            return expect(postMessage).toHaveBeenCalledWith({
                                action: 'response',
                                headers: { actionId: 'nanoid' },
                                id: 'nanoid',
                                name: 'api',
                                result: {
                                    data: { success: 'message' },
                                    headers: { actionId: 'nanoid' },
                                    status: 200
                                },
                                url: '/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk',
                                validationUrl: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk'
                            });
                        })];
                case 2:
                    _b.sent();
                    onmessageFromWorker({
                        action: CustomAction.RETURN_PROCESSED_API_REQUEST,
                        params: { id: 'nanoid', data: { changedData: 'hello' } }
                    });
                    _a = expect;
                    return [4 /*yield*/, response];
                case 3:
                    _a.apply(void 0, [_b.sent()]).toEqual({ changedData: 'hello' });
                    return [2 /*return*/];
            }
        });
    }); });
    it('should not fail if no options passed', function () { return __awaiter(void 0, void 0, void 0, function () {
        var params;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    setUp();
                    expect(initializeWebWorker).toHaveBeenCalledTimes(1);
                    params = {
                        name: RequestAction.API,
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk',
                        id: 'nanoid',
                        headers: { actionId: 'nanoid' }
                    };
                    requestProvider.request('/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk');
                    act(function () {
                        onmessageFromWorker({ action: CustomAction.REQUEST, params: params });
                    });
                    expect(postMessage).toHaveBeenCalledWith({
                        action: 'apiRequest',
                        data: undefined,
                        headers: { actionId: 'nanoid' },
                        id: 'nanoid',
                        method: 'GET',
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk'
                    });
                    return [4 /*yield*/, waitFor(function () { return expect(fetch).toHaveBeenCalledTimes(2); })];
                case 1:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    }); });
    it('should intercept request if requestUrl start with apiPath', function () { return __awaiter(void 0, void 0, void 0, function () {
        var params, response, _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    setUp();
                    expect(initializeWebWorker).toHaveBeenCalledTimes(1);
                    params = {
                        name: RequestAction.API,
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk',
                        id: 'nanoid',
                        headers: {
                            actionId: 'nanoid',
                            'Content-Type': 'application/json'
                        }
                    };
                    response = requestProvider.request('/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk', {
                        method: 'GET'
                    });
                    act(function () {
                        onmessageFromWorker({ action: CustomAction.REQUEST, params: params });
                    });
                    expect(postMessage).toHaveBeenCalledWith({
                        action: 'apiRequest',
                        data: undefined,
                        headers: {
                            actionId: 'nanoid'
                        },
                        id: 'nanoid',
                        method: 'GET',
                        url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk'
                    });
                    return [4 /*yield*/, waitFor(function () { return expect(fetch).toHaveBeenCalledTimes(2); })];
                case 1:
                    _b.sent();
                    expect(fetch).toHaveBeenCalledWith('https://tst-01.reltio.com/nui/services/getTokenForWorker', { method: 'GET' });
                    expect(fetch).toHaveBeenCalledWith('/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk', {
                        body: undefined,
                        headers: {
                            'Content-Type': 'application/json',
                            Authorization: 'Bearer some token',
                            actionId: 'nanoid'
                        },
                        noRetry: true,
                        noRequestProcessor: true,
                        method: 'GET'
                    });
                    return [4 /*yield*/, waitFor(function () {
                            return expect(postMessage).toHaveBeenCalledWith({
                                action: 'response',
                                headers: {
                                    actionId: 'nanoid',
                                    'Content-Type': 'application/json'
                                },
                                id: 'nanoid',
                                name: 'api',
                                result: {
                                    data: { success: 'message' },
                                    headers: {
                                        actionId: 'nanoid',
                                        'Content-Type': 'application/json'
                                    },
                                    status: 200
                                },
                                url: '/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk',
                                validationUrl: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR?options=sendHidden%2CaddRefAttrUriToCrosswalk'
                            });
                        })];
                case 2:
                    _b.sent();
                    onmessageFromWorker({
                        action: CustomAction.RETURN_PROCESSED_API_REQUEST,
                        params: { id: 'nanoid', data: { changedData: 'hello' } }
                    });
                    _a = expect;
                    return [4 /*yield*/, response];
                case 3:
                    _a.apply(void 0, [_b.sent()]).toEqual({ changedData: 'hello' });
                    return [2 /*return*/];
            }
        });
    }); });
    it('should intercept response', function () { return __awaiter(void 0, void 0, void 0, function () {
        var response, _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    setUp();
                    expect(initializeWebWorker).toHaveBeenCalledTimes(1);
                    response = requestProvider.request('/nui/reltioAPI/api/uitest1/entities/1Ms5ANeR/attributes/TextField/3U1mbeNRn?crosswalkValue=entity-FB-add-0.4094314985799944', {
                        method: 'GET'
                    });
                    return [4 /*yield*/, waitFor(function () {
                            return expect(postMessage).toHaveBeenCalledWith({
                                action: 'apiResponse',
                                data: { success: 'message' },
                                id: 'nanoid',
                                method: 'GET',
                                url: 'https://tst-01.reltio.com/reltio/api/uitest1/entities/1Ms5ANeR/attributes/TextField/3U1mbeNRn?crosswalkValue=entity-FB-add-0.4094314985799944'
                            });
                        })];
                case 1:
                    _b.sent();
                    onmessageFromWorker({
                        action: CustomAction.RETURN_PROCESSED_API_RESPONSE,
                        params: { id: 'nanoid', data: { changedData: 'hello' } }
                    });
                    _a = expect;
                    return [4 /*yield*/, response];
                case 2:
                    _a.apply(void 0, [_b.sent()]).toEqual({ changedData: 'hello' });
                    return [2 /*return*/];
            }
        });
    }); });
    it('should not initialize web worker with empty custom script', function () {
        var config = [{ files: [] }];
        setUp({ config: config });
        expect(initializeWebWorker).toHaveBeenCalledTimes(0);
    });
    it('should not initialize web worker with unsupported custom script', function () {
        var config = [
            {
                files: ['https://reltio-ui-files.s3.amazonaws.com/custom-scripts/hideAttr.js'],
                platform: CustomScriptPlatform.CLASSIC
            }
        ];
        setUp({ config: config });
        expect(initializeWebWorker).toHaveBeenCalledTimes(0);
    });
    it('should not initialize web worker without metadata', function () {
        var addInterceptorsSpy = jest.spyOn(interceptors, 'addInternalResponseInterceptor');
        var mdmValues = __assign(__assign({}, defaultMdmValues), { metadata: null });
        setUp({ mdmValues: mdmValues });
        expect(initializeWebWorker).toHaveBeenCalledTimes(0);
        expect(addInterceptorsSpy).toHaveBeenCalledTimes(0);
    });
    it('should call console.log on log event', function () {
        setUp();
        var message = 'some data to console';
        var log = jest.spyOn(console, 'log');
        act(function () {
            onmessageFromWorker({ action: CustomAction.LOG, params: message });
        });
        expect(log).toHaveBeenCalledWith(message);
        expect(log).toHaveBeenCalledTimes(1);
    });
    it('should set message on message event and clean on close popup', function () {
        setUp();
        var message = 'hello';
        act(function () {
            onmessageFromWorker({ action: CustomAction.MESSAGE, params: message });
        });
        expect(showSnackbarMessage).toHaveBeenCalledWith(message);
    });
    it('should not intercept request or response, if url is not process to api', function () { return __awaiter(void 0, void 0, void 0, function () {
        var response;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    setUp();
                    return [4 /*yield*/, requestProvider.request('/nui/reltioAPI/api/uitest1/_facets/someData', { method: 'GET' })];
                case 1:
                    response = _a.sent();
                    expect(postMessage).not.toHaveBeenCalled();
                    expect(fetch).toHaveBeenCalledWith('/nui/reltioAPI/api/uitest1/_facets/someData', { method: 'GET' });
                    expect(response).toEqual(result);
                    return [2 /*return*/];
            }
        });
    }); });
    it('should change url in request for absolute path in new UI correctly', function () { return __awaiter(void 0, void 0, void 0, function () {
        var result, customScriptWithAbsolutePath, params;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    result = { success: 'message' };
                    requestMock.mockImplementation(function (url, _options) {
                        var isTokenRequest = url.includes('getTokenForWorker');
                        return Promise.resolve(isTokenRequest ? { accessToken: 'some token' } : result);
                    });
                    customScriptWithAbsolutePath = {
                        files: ['https://reltio-ui-files.s3.amazonaws.com/custom-scripts/hideAttr.js'],
                        permissions: ['/entities']
                    };
                    setUp({ config: [customScriptWithAbsolutePath] });
                    expect(initializeWebWorker).toHaveBeenCalledTimes(1);
                    params = {
                        name: RequestAction.API,
                        url: '/entities',
                        method: 'POST',
                        id: 'nanoid',
                        headers: {
                            actionId: 'nanoid',
                            'Content-Type': 'application/json'
                        }
                    };
                    act(function () {
                        onmessageFromWorker({ action: CustomAction.REQUEST, params: params });
                    });
                    return [4 /*yield*/, waitFor(function () {
                            return expect(requestMock).toHaveBeenCalledWith('/nui/reltioAPI/api/uitest1/entities', {
                                body: undefined,
                                headers: {
                                    'Content-Type': 'application/json',
                                    actionId: 'nanoid',
                                    Authorization: 'Bearer some token'
                                },
                                noRequestProcessor: true,
                                noRetry: true,
                                method: 'POST'
                            });
                        })];
                case 1:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    }); });
    it('should resolve getEntityUri/getEntity with the current entity after navigation', function () {
        var entityA = { uri: 'entities/AAA', type: 'configuration/entityTypes/HCA', attributes: {} };
        var entityB = { uri: 'entities/BBB', type: 'configuration/entityTypes/HCA', attributes: {} };
        var updateMdmValues = setUp({ mdmValues: __assign(__assign({}, defaultMdmValues), { entity: entityA }) }).updateMdmValues;
        act(function () {
            onmessageFromWorker({
                action: CustomAction.REQUEST,
                params: { name: RequestAction.GET_ENTITY_URI, id: 'id1' }
            });
        });
        expect(postMessage).toHaveBeenLastCalledWith({
            action: 'response',
            result: 'entities/AAA',
            name: RequestAction.GET_ENTITY_URI,
            id: 'id1'
        });
        act(function () {
            updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { entity: entityB })); });
        });
        act(function () {
            onmessageFromWorker({
                action: CustomAction.REQUEST,
                params: { name: RequestAction.GET_ENTITY_URI, id: 'id2' }
            });
        });
        expect(postMessage).toHaveBeenLastCalledWith({
            action: 'response',
            result: 'entities/BBB',
            name: RequestAction.GET_ENTITY_URI,
            id: 'id2'
        });
        act(function () {
            onmessageFromWorker({ action: CustomAction.REQUEST, params: { name: RequestAction.GET_ENTITY, id: 'id3' } });
        });
        expect(postMessage).toHaveBeenLastCalledWith({
            action: 'response',
            result: entityB,
            name: RequestAction.GET_ENTITY,
            id: 'id3'
        });
    });
    it('should resolve getSearchQuery with the current query after it changes', function () {
        var updateMdmValues = setUp({
            mdmValues: __assign(__assign({}, defaultMdmValues), { searchProviderData: { type: 'search', data: { query: 'q1' } } })
        }).updateMdmValues;
        act(function () {
            onmessageFromWorker({
                action: CustomAction.REQUEST,
                params: { name: RequestAction.GET_SEARCH_QUERY, id: 'id1' }
            });
        });
        expect(postMessage).toHaveBeenLastCalledWith({
            action: 'response',
            result: 'q1',
            name: RequestAction.GET_SEARCH_QUERY,
            id: 'id1'
        });
        act(function () {
            updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { searchProviderData: { type: 'search', data: { query: 'q2' } } })); });
        });
        act(function () {
            onmessageFromWorker({
                action: CustomAction.REQUEST,
                params: { name: RequestAction.GET_SEARCH_QUERY, id: 'id2' }
            });
        });
        expect(postMessage).toHaveBeenLastCalledWith({
            action: 'response',
            result: 'q2',
            name: RequestAction.GET_SEARCH_QUERY,
            id: 'id2'
        });
    });
});
