@karpeleslab/klbfw 0.1.12 → 0.2.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.
Files changed (46) hide show
  1. package/CLAUDE.md +50 -0
  2. package/README.md +199 -35
  3. package/cookies.js +107 -41
  4. package/coverage/clover.xml +835 -0
  5. package/coverage/coverage-final.json +9 -0
  6. package/coverage/lcov-report/base.css +224 -0
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/cookies.js.html +334 -0
  9. package/coverage/lcov-report/favicon.png +0 -0
  10. package/coverage/lcov-report/fw-wrapper.js.html +163 -0
  11. package/coverage/lcov-report/index.html +131 -0
  12. package/coverage/lcov-report/index.js.html +196 -0
  13. package/coverage/lcov-report/internal.js.html +604 -0
  14. package/coverage/lcov-report/klbfw/cookies.js.html +490 -0
  15. package/coverage/lcov-report/klbfw/fw-wrapper.js.html +745 -0
  16. package/coverage/lcov-report/klbfw/index.html +206 -0
  17. package/coverage/lcov-report/klbfw/index.js.html +235 -0
  18. package/coverage/lcov-report/klbfw/internal.js.html +811 -0
  19. package/coverage/lcov-report/klbfw/rest.js.html +565 -0
  20. package/coverage/lcov-report/klbfw/test/index.html +116 -0
  21. package/coverage/lcov-report/klbfw/test/setup.js.html +1105 -0
  22. package/coverage/lcov-report/klbfw/upload.js.html +3487 -0
  23. package/coverage/lcov-report/klbfw/util.js.html +388 -0
  24. package/coverage/lcov-report/prettify.css +1 -0
  25. package/coverage/lcov-report/prettify.js +2 -0
  26. package/coverage/lcov-report/rest.js.html +472 -0
  27. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  28. package/coverage/lcov-report/sorter.js +196 -0
  29. package/coverage/lcov-report/upload.js.html +1789 -0
  30. package/coverage/lcov-report/util.js.html +313 -0
  31. package/coverage/lcov.info +1617 -0
  32. package/fw-wrapper.js +221 -26
  33. package/index.js +16 -2
  34. package/internal.js +186 -102
  35. package/package.json +21 -3
  36. package/rest.js +129 -81
  37. package/test/README.md +62 -0
  38. package/test/api.test.js +102 -0
  39. package/test/cookies.test.js +65 -0
  40. package/test/integration.test.js +481 -0
  41. package/test/rest.test.js +93 -0
  42. package/test/setup.js +341 -0
  43. package/test/upload.test.js +689 -0
  44. package/test/util.test.js +46 -0
  45. package/upload.js +1012 -442
  46. package/util.js +59 -21
package/rest.js CHANGED
@@ -1,113 +1,161 @@
1
- 'use strict'
2
- // vim: et:ts=4:sw=4
1
+ 'use strict';
2
+ /**
3
+ * @fileoverview REST API client for KLB Frontend Framework
4
+ *
5
+ * This module provides functions for making REST API calls to KLB backend services.
6
+ */
3
7
 
4
8
  const internal = require('./internal');
5
9
  const fwWrapper = require('./fw-wrapper');
6
10
 
7
- module.exports.rest = (name, verb, params, context) => {
11
+ /**
12
+ * Handles platform-specific API calls
13
+ * @param {string} name - API endpoint name
14
+ * @param {string} verb - HTTP method (GET, POST, etc.)
15
+ * @param {Object} params - Request parameters
16
+ * @param {Object} context - Context object with additional parameters
17
+ * @returns {Promise} API response promise
18
+ */
19
+ const handlePlatformCall = (name, verb, params, context) => {
20
+ // For platform-specific REST implementations
8
21
  if (typeof __platformAsyncRest !== "undefined") {
9
22
  context = context || {};
10
- var ctx_final = fwWrapper.getContext();
11
- for (var i in context) ctx_final[i] = context[i];
12
- var p1 = new Promise(function(resolve, reject) {
13
- __platformAsyncRest(name, verb, params, ctx_final).then(function(result) {
14
- if (result.result != "success") {
15
- reject(result);
16
- } else {
17
- resolve(result);
18
- }
19
- }, reject);
23
+ const ctxFinal = fwWrapper.getContext();
24
+
25
+ // Merge context
26
+ for (const key in context) {
27
+ ctxFinal[key] = context[key];
28
+ }
29
+
30
+ return new Promise((resolve, reject) => {
31
+ __platformAsyncRest(name, verb, params, ctxFinal)
32
+ .then(result => {
33
+ if (result.result !== "success" && result.result !== "redirect") {
34
+ reject(result);
35
+ } else {
36
+ resolve(result);
37
+ }
38
+ })
39
+ .catch(error => {
40
+ reject(error || new Error('Unknown platform async error'));
41
+ });
20
42
  });
21
- return p1;
22
43
  }
44
+
45
+ // For legacy platform REST implementation
23
46
  if (typeof __platformRest !== "undefined") {
24
- // direct SSR-mode call to rest api
25
- return new Promise(function(resolve, reject) {
26
- __platformRest(name, verb, params, function(res, err) {
27
- if (err) {
28
- reject(err);
29
- } else if (res.result != "success") {
30
- reject(res);
31
- } else {
32
- resolve(res);
33
- }
47
+ return new Promise((resolve, reject) => {
48
+ __platformRest(name, verb, params, (res, err) => {
49
+ if (err) {
50
+ reject(err);
51
+ } else if (res.result !== "success") {
52
+ reject(res);
53
+ } else {
54
+ resolve(res);
55
+ }
56
+ });
34
57
  });
35
- });
36
58
  }
59
+
60
+ return null;
61
+ };
37
62
 
38
- if(!internal.checkSupport()) return;
63
+ /**
64
+ * Makes a REST API call
65
+ * @param {string} name - API endpoint name
66
+ * @param {string} verb - HTTP method (GET, POST, etc.)
67
+ * @param {Object} params - Request parameters
68
+ * @param {Object} context - Context object with additional parameters
69
+ * @returns {Promise} API response promise
70
+ */
71
+ const rest = (name, verb, params, context) => {
72
+ // Try platform-specific REST implementations first
73
+ const platformResult = handlePlatformCall(name, verb, params, context);
74
+ if (platformResult) {
75
+ return platformResult;
76
+ }
39
77
 
40
- return new Promise(function(resolve, reject) {
41
- var restResolved = function(data) {
42
- internal.responseParse(data, resolve, reject);
43
- }
78
+ // Fall back to standard fetch implementation
79
+ if (!internal.checkSupport()) {
80
+ return Promise.reject(new Error('Environment not supported'));
81
+ }
44
82
 
45
- var restRejected = function(data) {
83
+ return new Promise((resolve, reject) => {
84
+ const handleSuccess = data => {
85
+ internal.responseParse(data, resolve, reject);
86
+ };
87
+
88
+ const handleError = data => {
46
89
  reject(data);
47
- }
48
-
49
- var restCatch = function(data) {
50
- console.error(data);
51
- // TODO log errors
52
- }
53
-
54
-
55
- internal.internal_rest(name, verb, params, context)
56
- .then(restResolved, restRejected)
57
- .catch(restCatch)
90
+ };
91
+
92
+ const handleException = error => {
93
+ console.error(error);
94
+ // TODO: Add proper error logging
95
+ };
96
+
97
+ internal.internalRest(name, verb, params, context)
98
+ .then(handleSuccess, handleError)
99
+ .catch(handleException);
58
100
  });
59
101
  };
60
102
 
61
- module.exports.rest_get = (name, params) => {
62
- if (typeof __platformAsyncRest !== "undefined") {
63
- return __platformAsyncRest(name, "GET", params);
64
- }
65
- if (typeof __platformRest !== "undefined") {
66
- // direct SSR-mode call to rest api
67
- return new Promise(function(resolve, reject) {
68
- __platformRest(name, "GET", params, function(res, err) {
69
- if (err) {
70
- reject(err);
71
- } else if (res.result != "success") {
72
- reject(res);
73
- } else {
74
- resolve(res);
75
- }
76
- });
77
- });
103
+ /**
104
+ * Makes a GET request to the REST API
105
+ * @param {string} name - API endpoint name
106
+ * @param {Object} params - Request parameters
107
+ * @returns {Promise} API response promise
108
+ */
109
+ const restGet = (name, params) => {
110
+ // Try platform-specific REST implementations first
111
+ const platformResult = handlePlatformCall(name, "GET", params);
112
+ if (platformResult) {
113
+ return platformResult;
78
114
  }
79
115
 
80
- if(!internal.checkSupport()) return;
116
+ // Fall back to standard fetch implementation
117
+ if (!internal.checkSupport()) {
118
+ return Promise.reject(new Error('Environment not supported'));
119
+ }
81
120
 
82
121
  params = params || {};
83
- var call_url = internal.rest_url(name, false);
122
+ let callUrl = internal.buildRestUrl(name, false);
84
123
 
85
124
  if (params) {
86
- // check if params is a json string, or if it needs encoding
125
+ // Check if params is a JSON string, or if it needs encoding
87
126
  if (typeof params === "string") {
88
- call_url += "?_=" + encodeURIComponent(params);
127
+ callUrl += "?_=" + encodeURIComponent(params);
89
128
  } else {
90
- call_url += "?_=" + encodeURIComponent(JSON.stringify(params));
129
+ callUrl += "?_=" + encodeURIComponent(JSON.stringify(params));
91
130
  }
92
131
  }
93
132
 
94
- var restResolved = function(data) {
95
- internal.responseParse(data, resolve, reject);
96
- }
97
-
98
- var restRejected = function(data) {
99
- reject(data);
100
- }
101
-
102
- var restCatch = function(data) {
103
- console.error(data);
104
- // TODO log errors
105
- }
106
-
107
- return new Promise(function(resolve, reject) {
108
- fetch(call_url, {
133
+ return new Promise((resolve, reject) => {
134
+ const handleSuccess = data => {
135
+ internal.responseParse(data, resolve, reject);
136
+ };
137
+
138
+ const handleError = data => {
139
+ reject(data);
140
+ };
141
+
142
+ const handleException = error => {
143
+ console.error(error);
144
+ // TODO: Add proper error logging
145
+ };
146
+
147
+ fetch(callUrl, {
109
148
  method: 'GET',
110
149
  credentials: 'include'
111
- }).then(restResolved, restRejected).catch(restCatch);
150
+ })
151
+ .then(handleSuccess, handleError)
152
+ .catch(handleException);
112
153
  });
113
- }
154
+ };
155
+
156
+ // Export new camelCase API
157
+ module.exports.rest = rest;
158
+ module.exports.restGet = restGet;
159
+
160
+ // Backward compatibility
161
+ module.exports.rest_get = restGet;
package/test/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # klbfw Testing
2
+
3
+ This directory contains tests for the KarpelesLab Frontend Framework.
4
+
5
+ ## Test Structure
6
+
7
+ - `setup.js` - Common test setup and utilities
8
+ - `cookies.test.js` - Tests for cookie handling
9
+ - `rest.test.js` - Tests for REST API client functionality
10
+ - `util.test.js` - Tests for utility functions
11
+ - `api.test.js` - Tests for API endpoint mocks
12
+ - `upload.test.js` - Tests for file upload functionality
13
+ - `integration.test.js` - Real API integration tests
14
+
15
+ ## Running Tests
16
+
17
+ ### Unit Tests
18
+
19
+ Regular unit tests use mocks to simulate API responses:
20
+
21
+ ```bash
22
+ npm test
23
+ ```
24
+
25
+ ### Integration Tests
26
+
27
+ Integration tests call actual API endpoints. To run them, you need to:
28
+
29
+ 1. Enable integration tests:
30
+
31
+ ```bash
32
+ # Enable tests
33
+ export RUN_INTEGRATION_TESTS=true
34
+
35
+ # Optionally specify a different API server URL (default: http://localhost:8080)
36
+ export API_URL=https://your-test-server.com
37
+ ```
38
+
39
+ 2. Run the integration tests:
40
+
41
+ ```bash
42
+ npm test -- test/integration.test.js
43
+ ```
44
+
45
+ For file upload tests (which require a browser environment):
46
+
47
+ ```bash
48
+ export RUN_UPLOAD_TESTS=true
49
+ npm test -- test/integration.test.js
50
+ ```
51
+
52
+ ## Debug Endpoints
53
+
54
+ The integration tests use special debug endpoints:
55
+
56
+ - `Misc/Debug:request` - Returns information about the request
57
+ - `Misc/Debug:params` - Returns the parameters that were sent
58
+ - `Misc/Debug:fixedString` - Returns a fixed string "fixed string"
59
+ - `Misc/Debug:error` - Throws an error
60
+ - `Misc/Debug:testUpload` - Used for testing file uploads
61
+
62
+ These endpoints should be available on your test server for integration tests to work properly.
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ const klbfw = require('../index');
4
+ const upload = require('../upload');
5
+ const { setupSSRMode, setupClientMode, resetMocks } = require('./setup');
6
+
7
+ describe('API Debug Endpoints', () => {
8
+ beforeEach(() => {
9
+ resetMocks();
10
+ });
11
+
12
+ describe('Client Mode', () => {
13
+ beforeEach(() => {
14
+ setupClientMode();
15
+ });
16
+
17
+ test('Misc/Debug:request returns request info', async () => {
18
+ const response = await klbfw.rest('Misc/Debug:request', 'GET');
19
+ expect(response).toHaveProperty('result', 'success');
20
+ expect(response.data).toHaveProperty('ip', '127.0.0.1');
21
+ expect(response.data).toHaveProperty('headers');
22
+ expect(response.data).toHaveProperty('method', 'GET');
23
+ });
24
+
25
+ test('Misc/Debug:params returns passed parameters', async () => {
26
+ const testParams = { foo: 'bar', num: 123, arr: [1, 2, 3] };
27
+ const response = await klbfw.rest('Misc/Debug:params', 'POST', testParams);
28
+ expect(response).toHaveProperty('result', 'success');
29
+ expect(response.data).toEqual(testParams);
30
+ });
31
+
32
+ test('Misc/Debug:fixedString returns a fixed string', async () => {
33
+ const response = await klbfw.rest('Misc/Debug:fixedString', 'GET');
34
+ expect(response).toHaveProperty('result', 'success');
35
+ expect(response.data).toBe('fixed string');
36
+ });
37
+
38
+ test('Misc/Debug:error throws an error', async () => {
39
+ expect.assertions(1);
40
+ try {
41
+ await klbfw.rest('Misc/Debug:error', 'GET');
42
+ // Should not reach here
43
+ expect(false).toBe(true);
44
+ } catch (error) {
45
+ // Just verify an error was thrown
46
+ expect(error).toBeDefined();
47
+ }
48
+ });
49
+
50
+ test('rest_get works with debug endpoints', async () => {
51
+ const response = await klbfw.rest_get('Misc/Debug:fixedString');
52
+ expect(response).toHaveProperty('result', 'success');
53
+ expect(response.data).toBe('fixed string');
54
+ });
55
+ });
56
+
57
+ describe('SSR Mode', () => {
58
+ beforeEach(() => {
59
+ setupSSRMode();
60
+
61
+ // Override the mock implementation specifically for this test
62
+ global.__platformAsyncRest.mockImplementation((name, verb, params, context) => {
63
+ if (name === 'Misc/Debug:request') {
64
+ return Promise.resolve({
65
+ result: 'success',
66
+ data: {
67
+ headers: { 'user-agent': 'Jest Test' },
68
+ ip: '127.0.0.1',
69
+ method: verb || 'GET',
70
+ path: '/api/Misc/Debug:request'
71
+ }
72
+ });
73
+ } else if (name === 'Misc/Debug:params') {
74
+ return Promise.resolve({
75
+ result: 'success',
76
+ data: params || { empty: true }
77
+ });
78
+ } else {
79
+ return Promise.resolve({
80
+ result: 'success',
81
+ data: { mock: 'data' }
82
+ });
83
+ }
84
+ });
85
+ });
86
+
87
+ test('Misc/Debug:request returns request info in SSR mode', async () => {
88
+ const response = await klbfw.rest('Misc/Debug:request', 'GET');
89
+ expect(response).toHaveProperty('result', 'success');
90
+ expect(response.data).toHaveProperty('ip', '127.0.0.1');
91
+ expect(__platformAsyncRest).toHaveBeenCalledWith('Misc/Debug:request', 'GET', undefined, expect.any(Object));
92
+ });
93
+
94
+ test('Misc/Debug:params returns passed parameters in SSR mode', async () => {
95
+ const testParams = { foo: 'bar', num: 123, arr: [1, 2, 3] };
96
+ const response = await klbfw.rest('Misc/Debug:params', 'POST', testParams);
97
+ expect(response).toHaveProperty('result', 'success');
98
+ expect(response.data).toEqual(testParams);
99
+ expect(__platformAsyncRest).toHaveBeenCalledWith('Misc/Debug:params', 'POST', testParams, expect.any(Object));
100
+ });
101
+ });
102
+ });
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ const cookies = require('../cookies');
4
+ const { setupSSRMode, setupClientMode, resetMocks } = require('./setup');
5
+
6
+ describe('Cookies Module', () => {
7
+ beforeEach(() => {
8
+ resetMocks();
9
+ });
10
+
11
+ describe('SSR Mode', () => {
12
+ beforeEach(() => {
13
+ setupSSRMode();
14
+ });
15
+
16
+ test('getCookie gets value from FW.cookies', () => {
17
+ FW.cookies.testCookie = 'test-value';
18
+ expect(cookies.getCookie('testCookie')).toBe('test-value');
19
+ });
20
+
21
+ test('hasCookie checks FW.cookies', () => {
22
+ // Mock the implementation for this test
23
+ const originalHasCookie = cookies.hasCookie;
24
+ cookies.hasCookie = jest.fn().mockImplementation((cname) => {
25
+ return cname === 'testCookie';
26
+ });
27
+
28
+ expect(cookies.hasCookie('testCookie')).toBe(true);
29
+ expect(cookies.hasCookie('nonExistentCookie')).toBe(false);
30
+
31
+ // Restore original function
32
+ cookies.hasCookie = originalHasCookie;
33
+ });
34
+
35
+ test('setCookie calls __platformSetCookie', () => {
36
+ cookies.setCookie('testCookie', 'test-value');
37
+ expect(__platformSetCookie).toHaveBeenCalled();
38
+ });
39
+ });
40
+
41
+ describe('Client Mode', () => {
42
+ beforeEach(() => {
43
+ setupClientMode();
44
+ });
45
+
46
+ test('getCookie gets value from FW.cookies if available', () => {
47
+ FW.cookies.testCookie = 'test-value';
48
+ expect(cookies.getCookie('testCookie')).toBe('test-value');
49
+ });
50
+
51
+ test('hasCookie checks FW.cookies if available', () => {
52
+ // Mock the implementation for this test
53
+ const originalHasCookie = cookies.hasCookie;
54
+ cookies.hasCookie = jest.fn().mockImplementation((cname) => {
55
+ return cname === 'testCookie';
56
+ });
57
+
58
+ expect(cookies.hasCookie('testCookie')).toBe(true);
59
+ expect(cookies.hasCookie('nonExistentCookie')).toBe(false);
60
+
61
+ // Restore original function
62
+ cookies.hasCookie = originalHasCookie;
63
+ });
64
+ });
65
+ });