@sasjs/cli 5.1.1 → 6.1.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.
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ 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);
28
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
+ 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;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ var __read = (this && this.__read) || function (o, n) {
54
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
55
+ if (!m) return o;
56
+ var i = m.call(o), r, ar = [], e;
57
+ try {
58
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
59
+ }
60
+ catch (error) { e = { error: error }; }
61
+ finally {
62
+ try {
63
+ if (r && !r.done && (m = i["return"])) m.call(i);
64
+ }
65
+ finally { if (e) throw e.error; }
66
+ }
67
+ return ar;
68
+ };
69
+ Object.defineProperty(exports, "__esModule", { value: true });
70
+ var FakeXHR = /** @class */ (function () {
71
+ function FakeXHR() {
72
+ this.listeners = {};
73
+ this.responseType = '';
74
+ this.status = 0;
75
+ this.statusText = '';
76
+ this.readyState = 0;
77
+ this.responseText = '';
78
+ this.response = null;
79
+ this.sentWith = null;
80
+ this.openedWith = null;
81
+ this.nativeSendCount = 0;
82
+ }
83
+ FakeXHR.prototype.open = function (method, url) {
84
+ this.openedWith = { method: method, url: url };
85
+ };
86
+ FakeXHR.prototype.send = function (body) {
87
+ // The bridge captures this as the "native" transport, so counting here is
88
+ // how a test sees whether a request reached the real one or an interceptor.
89
+ this.nativeSendCount++;
90
+ this.sentWith = body;
91
+ };
92
+ FakeXHR.prototype.addEventListener = function (type, fn) {
93
+ this.listeners[type] = this.listeners[type] || [];
94
+ this.listeners[type].push(fn);
95
+ };
96
+ FakeXHR.prototype.dispatchEvent = function (event) {
97
+ ;
98
+ (this.listeners[event.type] || []).forEach(function (fn) { return fn(event); });
99
+ return true;
100
+ };
101
+ FakeXHR.prototype.getAllResponseHeaders = function () {
102
+ return '';
103
+ };
104
+ FakeXHR.prototype.getResponseHeader = function (_key) {
105
+ return null;
106
+ };
107
+ return FakeXHR;
108
+ }());
109
+ var loadBridge = function (fetchImpl) {
110
+ jest.resetModules();
111
+ // A fresh subclass per load: the bridge patches XMLHttpRequest.prototype, so
112
+ // reusing one class would leave the previous bridge's wrapper in place as the
113
+ // "native" transport it captures, and stack an interceptor per load.
114
+ var XHR = /** @class */ (function (_super) {
115
+ __extends(XHR, _super);
116
+ function XHR() {
117
+ return _super !== null && _super.apply(this, arguments) || this;
118
+ }
119
+ return XHR;
120
+ }(FakeXHR));
121
+ var window = {};
122
+ global.window = window;
123
+ global.localStorage = { getItem: function () { return null; } };
124
+ global.XMLHttpRequest = XHR;
125
+ global.Event = /** @class */ (function () {
126
+ function class_1(type) {
127
+ this.type = type;
128
+ }
129
+ return class_1;
130
+ }());
131
+ global.ProgressEvent = /** @class */ (function () {
132
+ function class_2(type) {
133
+ this.type = type;
134
+ }
135
+ return class_2;
136
+ }());
137
+ global.fetch = fetchImpl;
138
+ require('../assets/intercept.js');
139
+ return { window: window, XHR: XHR };
140
+ };
141
+ var jsonResponse = function (body, status) {
142
+ if (status === void 0) { status = 200; }
143
+ return new Response(body, {
144
+ status: status,
145
+ headers: { 'content-type': 'application/json' }
146
+ });
147
+ };
148
+ var run = function (xhr, method, url, body) { return __awaiter(void 0, void 0, void 0, function () {
149
+ var loaded;
150
+ return __generator(this, function (_a) {
151
+ switch (_a.label) {
152
+ case 0:
153
+ loaded = new Promise(function (resolve) { return xhr.addEventListener('load', resolve); });
154
+ xhr.open(method, url);
155
+ xhr.send(body);
156
+ return [4 /*yield*/, loaded];
157
+ case 1:
158
+ _a.sent();
159
+ return [2 /*return*/];
160
+ }
161
+ });
162
+ }); };
163
+ describe('page-side bridge', function () {
164
+ afterEach(function () {
165
+ delete global.window;
166
+ delete global.localStorage;
167
+ delete global.XMLHttpRequest;
168
+ delete global.fetch;
169
+ });
170
+ it('leaves a request outside the SASjs API to the native transport', function () { return __awaiter(void 0, void 0, void 0, function () {
171
+ var fetchImpl, XHR, xhr;
172
+ return __generator(this, function (_a) {
173
+ fetchImpl = jest.fn();
174
+ XHR = loadBridge(fetchImpl).XHR;
175
+ xhr = new XHR();
176
+ xhr.open('GET', '/api/health');
177
+ xhr.send();
178
+ expect(xhr.sentWith).toBeUndefined();
179
+ expect(fetchImpl).not.toHaveBeenCalled();
180
+ return [2 /*return*/];
181
+ });
182
+ }); });
183
+ it('converts a multipart body to JSON and replays the response onto the XHR', function () { return __awaiter(void 0, void 0, void 0, function () {
184
+ var fetchImpl, XHR, form, xhr, _a, init;
185
+ return __generator(this, function (_b) {
186
+ switch (_b.label) {
187
+ case 0:
188
+ fetchImpl = jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
189
+ return [2 /*return*/, jsonResponse('{"status":"success"}')];
190
+ }); }); });
191
+ XHR = loadBridge(fetchImpl).XHR;
192
+ form = new FormData();
193
+ form.append('_program', '/Public/app/test/services/public/getdata');
194
+ form.append('table1', new Blob(['a,b\n1,2\n'], { type: 'text/csv' }), 'data.csv');
195
+ xhr = new XHR();
196
+ return [4 /*yield*/, run(xhr, 'POST', '/SASjsApi/stp/execute', form)];
197
+ case 1:
198
+ _b.sent();
199
+ _a = __read(fetchImpl.mock.calls[0], 2), init = _a[1];
200
+ expect(init.method).toBe('POST');
201
+ expect(init.headers['content-type']).toBe('application/json');
202
+ expect(JSON.parse(init.body)).toEqual({
203
+ params: { _program: '/Public/app/test/services/public/getdata' },
204
+ tables: [{ name: 'table1', filename: 'data.csv', csv: 'a,b\n1,2\n' }]
205
+ });
206
+ expect(xhr.status).toBe(200);
207
+ expect(xhr.readyState).toBe(4);
208
+ expect(xhr.responseText).toBe('{"status":"success"}');
209
+ return [2 /*return*/];
210
+ }
211
+ });
212
+ }); });
213
+ it('parses the response when the app asked for json', function () { return __awaiter(void 0, void 0, void 0, function () {
214
+ var XHR, xhr;
215
+ return __generator(this, function (_a) {
216
+ switch (_a.label) {
217
+ case 0:
218
+ XHR = loadBridge(jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
219
+ return [2 /*return*/, jsonResponse('{"a":1}')];
220
+ }); }); })).XHR;
221
+ xhr = new XHR();
222
+ xhr.responseType = 'json';
223
+ return [4 /*yield*/, run(xhr, 'GET', '/SASjsApi/info')];
224
+ case 1:
225
+ _a.sent();
226
+ expect(xhr.response).toEqual({ a: 1 });
227
+ return [2 /*return*/];
228
+ }
229
+ });
230
+ }); });
231
+ it('answers with a 500 body when the request itself fails', function () { return __awaiter(void 0, void 0, void 0, function () {
232
+ var consoleError, XHR, xhr;
233
+ return __generator(this, function (_a) {
234
+ switch (_a.label) {
235
+ case 0:
236
+ consoleError = jest.spyOn(console, 'error').mockImplementation();
237
+ XHR = loadBridge(jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
238
+ return [2 /*return*/, Promise.reject(new Error('worker died'))];
239
+ }); }); })).XHR;
240
+ xhr = new XHR();
241
+ return [4 /*yield*/, run(xhr, 'GET', '/SASjsApi/info')];
242
+ case 1:
243
+ _a.sent();
244
+ expect(xhr.status).toBe(500);
245
+ expect(JSON.parse(xhr.responseText)).toEqual({
246
+ message: 'worker died',
247
+ error: 'worker died'
248
+ });
249
+ consoleError.mockRestore();
250
+ return [2 /*return*/];
251
+ }
252
+ });
253
+ }); });
254
+ it('gives each load a fresh prototype, so a request traverses one interceptor', function () {
255
+ var first = loadBridge(jest.fn());
256
+ var second = loadBridge(jest.fn());
257
+ // The bridge patches XMLHttpRequest.prototype and captures what it found
258
+ // there as the native transport. Loading into a class that is already
259
+ // patched would make the second load's "native" the first load's wrapper.
260
+ expect(second.XHR.prototype).not.toBe(first.XHR.prototype);
261
+ expect(Object.getPrototypeOf(second.XHR.prototype)).toBe(FakeXHR.prototype);
262
+ var xhr = new second.XHR();
263
+ xhr.open('GET', '/api/health');
264
+ xhr.send();
265
+ expect(xhr.nativeSendCount).toBe(1);
266
+ });
267
+ });