mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-05 03:30:05 +00:00
Compare commits
129 Commits
suggestion
...
v1.3.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5de6b50a9 | ||
|
|
f1184346d8 | ||
|
|
337fe5e988 | ||
|
|
c23e6352e2 | ||
|
|
57ce0ae35f | ||
|
|
115caec761 | ||
|
|
c626c57662 | ||
|
|
82b14fad14 | ||
|
|
755bf7d07c | ||
|
|
619833db29 | ||
|
|
a587d9f6de | ||
|
|
83fb89f779 | ||
|
|
6b98bbce7c | ||
|
|
3c2487e423 | ||
|
|
0d52af5375 | ||
|
|
d0da343efc | ||
|
|
54f401a319 | ||
|
|
5efcb11b7d | ||
|
|
929d7b993b | ||
|
|
688221c042 | ||
|
|
57d0b30f47 | ||
| 0d5af2487d | |||
| 1ea163fd03 | |||
|
|
f27444bc52 | ||
|
|
de426c9a92 | ||
|
|
a006ead205 | ||
|
|
422c2a1fd5 | ||
|
|
0c6409e402 | ||
|
|
68b864cf75 | ||
|
|
75a11cdff4 | ||
|
|
4e2b6d32cc | ||
|
|
cd9757b383 | ||
|
|
fb727788d0 | ||
|
|
35eb6c4935 | ||
|
|
ea0f338b90 | ||
|
|
b6a17b39b9 | ||
|
|
9ed64e5a2c | ||
|
|
0479a5d651 | ||
|
|
005f10bb47 | ||
|
|
98c9cb78ff | ||
|
|
8192f69f67 | ||
|
|
c28a8ebf15 | ||
|
|
a409d8cdb6 | ||
|
|
618a20eaba | ||
|
|
c9b1273c31 | ||
|
|
59674744be | ||
|
|
870cc0055b | ||
|
|
0ffa62fab4 | ||
|
|
b4c7868fb6 | ||
|
|
2266578013 | ||
|
|
f2ebe1a5b0 | ||
|
|
6a52bbe560 | ||
|
|
a5c725e677 | ||
|
|
f5e1907e28 | ||
|
|
f7a9b0cbb6 | ||
|
|
1258a1a180 | ||
|
|
0bb343a1de | ||
|
|
929c89b70f | ||
|
|
169ca35238 | ||
|
|
60be28f149 | ||
|
|
14daa55184 | ||
|
|
f763f05b5e | ||
|
|
b6aced5bad | ||
|
|
7bb7db0f27 | ||
|
|
36ea148446 | ||
|
|
762254d8c4 | ||
|
|
8474b222ea | ||
|
|
c1750c014e | ||
|
|
d7a7909529 | ||
|
|
31b60a985e | ||
|
|
a6b13d9cb9 | ||
|
|
55fcbf2e36 | ||
|
|
fad8549d92 | ||
|
|
95c03e5d07 | ||
|
|
e241a47c23 | ||
|
|
ed7f36dbed | ||
|
|
74f0c263db | ||
|
|
5a2a4bf39c | ||
|
|
0ea91ddd3b | ||
|
|
4422c37827 | ||
|
|
5fad9d01bc | ||
|
|
fb02c77a3a | ||
|
|
5de84c07a8 | ||
|
|
54e2319183 | ||
|
|
261913d2d7 | ||
|
|
187917cb32 | ||
|
|
d22b6c77b3 | ||
|
|
785b276741 | ||
|
|
2ce3669b10 | ||
|
|
3a1ea1614f | ||
|
|
3c5988aacf | ||
|
|
1a25c354fa | ||
|
|
eb1668d6c1 | ||
|
|
69d088a9c6 | ||
|
|
ccd44c31c7 | ||
|
|
dec7c18ecb | ||
|
|
c8a5eb5993 | ||
|
|
700a67a600 | ||
|
|
a2778bed52 | ||
|
|
2b04fe0c3e | ||
|
|
1c1e6b8efd | ||
|
|
c92a0a53f2 | ||
|
|
ee024c67ab | ||
|
|
27301651be | ||
|
|
9742f53dde | ||
|
|
38a950a036 | ||
|
|
e41c54e37e | ||
|
|
405a19f0cf | ||
|
|
c7d6c66093 | ||
|
|
8bf74d17e9 | ||
|
|
a12244cf78 | ||
|
|
a579f481c5 | ||
|
|
2ecd57169f | ||
|
|
7d84033ad4 | ||
|
|
c4109b225b | ||
|
|
2ec8696615 | ||
|
|
c5339276e4 | ||
|
|
97db960f62 | ||
|
|
0ac7f8892e | ||
|
|
68f5e5bec5 | ||
|
|
69a14ff6d7 | ||
|
|
70e461224a | ||
|
|
4da22ee6b4 | ||
|
|
2c8ba09578 | ||
|
|
c12d6f1c9c | ||
|
|
92504b0c16 | ||
|
|
e5fb7a7698 | ||
|
|
c22b9066d8 | ||
|
|
77d7e03de5 |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -21,7 +21,11 @@ jobs:
|
|||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- name: Install Dependencies
|
||||||
- run: npm run package:lib
|
run: npm ci
|
||||||
|
- name: Check code style
|
||||||
|
run: npm run lint
|
||||||
|
- name: Build Package
|
||||||
|
run: npm run package:lib
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
|
|||||||
4
.github/workflows/npmpublish.yml
vendored
4
.github/workflows/npmpublish.yml
vendored
@@ -6,7 +6,7 @@ name: SASjs Build and Publish
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -16,6 +16,8 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
- name: Check code style
|
||||||
|
run: npm run lint
|
||||||
- name: Build Project
|
- name: Build Project
|
||||||
run: npm run build
|
run: npm run build
|
||||||
- name: Semantic Release
|
- name: Semantic Release
|
||||||
|
|||||||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ Tests are run using cypress. Before running tests, you need to define the follow
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/main/mc_all.sas?_=1";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
filename ft15f001 temp;
|
filename ft15f001 temp;
|
||||||
parmcards4;
|
parmcards4;
|
||||||
@@ -40,18 +40,13 @@ parmcards4;
|
|||||||
# Viya
|
# Viya
|
||||||
|
|
||||||
```
|
```
|
||||||
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/main/mc_all.sas";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
filename ft15f001 temp;
|
filename ft15f001 temp;
|
||||||
parmcards4;
|
parmcards4;
|
||||||
|
%webout(FETCH)
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%global sasjs_tables;
|
|
||||||
%let sasjs_tables=&sasjs_tables;
|
|
||||||
%put &=sasjs_tables;
|
|
||||||
%let sasjs_tables=&sasjs_tables;
|
|
||||||
%macro x();
|
%macro x();
|
||||||
%global sasjs_tables;
|
|
||||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||||
%let table=%scan(&sasjs_tables,&i);
|
%let table=%scan(&sasjs_tables,&i);
|
||||||
%webout(OBJ,&table)
|
%webout(OBJ,&table)
|
||||||
@@ -60,13 +55,11 @@ parmcards4;
|
|||||||
%x()
|
%x()
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mv_createwebservice(path=/Public/app/common,name=sendObj)
|
%mp_createwebservice(path=/Public/app/common,name=sendObj)
|
||||||
filename ft15f001 temp;
|
filename ft15f001 temp;
|
||||||
parmcards4;
|
parmcards4;
|
||||||
|
%webout(FETCH)
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%global sasjs_tables;
|
|
||||||
%let sasjs_tables=&sasjs_tables;
|
|
||||||
%put &=sasjs_tables;
|
|
||||||
%macro x();
|
%macro x();
|
||||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||||
%let table=%scan(&sasjs_tables,&i);
|
%let table=%scan(&sasjs_tables,&i);
|
||||||
@@ -76,7 +69,15 @@ parmcards4;
|
|||||||
%x()
|
%x()
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mv_createwebservice(path=/Public/app/common,name=sendArr)
|
%mp_createwebservice(path=/Public/app/common,name=sendArr)
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
If you can keep your head when all about you
|
||||||
|
Are losing theirs and blaming it on you,
|
||||||
|
If you can trust yourself when all men doubt you,
|
||||||
|
But make allowance for their doubting too;
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=makeErr)
|
||||||
```
|
```
|
||||||
|
|
||||||
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
|
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ SASjs is a open-source framework for building Web Apps on SAS® platforms. You c
|
|||||||
|
|
||||||
3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash.
|
3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash.
|
||||||
|
|
||||||
If you are short on time and just need to build an app quickly, then check out [this video](https://vimeo.com/393161794) and the [react-seed-app](https://github.com/macropeople/react-seed-app) which provides some boilerplate.
|
If you are short on time and just need to build an app quickly, then check out [this video](https://vimeo.com/393161794) and the [react-seed-app](https://github.com/sasjs/react-seed-app) which provides some boilerplate.
|
||||||
|
|
||||||
For more information on building web apps with SAS, check out [sasjs.io](https://sasjs.io)
|
For more information on building web apps with SAS, check out [sasjs.io](https://sasjs.io)
|
||||||
|
|
||||||
## None of this makes sense. How do I build an app with it?
|
## None of this makes sense. How do I build an app with it?
|
||||||
|
|
||||||
Ok ok. Deploy this [example.html](https://github.com/sasjs/adapter/blob/main/example.html) file to your web server, and update `servertype` to `SAS9` or `SASVIYA` depending on your backend.
|
Ok ok. Deploy this [example.html](https://raw.githubusercontent.com/sasjs/adapter/master/example.html) file to your web server, and update `servertype` to `SAS9` or `SASVIYA` depending on your backend.
|
||||||
|
|
||||||
The backend part can be deployed as follows:
|
The backend part can be deployed as follows:
|
||||||
|
|
||||||
@@ -43,6 +43,6 @@ You now have a simple web app with a backend service!
|
|||||||
|
|
||||||
# More resources
|
# More resources
|
||||||
|
|
||||||
For more information specific to this adapter you can check out this [user guide](https://sasjs.io/sasjs/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
|
For more information specific to this adapter you can check out this [user guide](https://sasjs.io/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
|
||||||
|
|
||||||
For more information on building web apps in general, check out these [resources](https://sasjs.io/training/resources/) or contact the [author](https://www.linkedin.com/in/allanbowe/) directly.
|
For more information on building web apps in general, check out these [resources](https://sasjs.io/training/resources/) or contact the [author](https://www.linkedin.com/in/allanbowe/) directly.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
294
docs/classes/reflection-560.reflection-1.sas9apiclient.html
Normal file
294
docs/classes/reflection-560.reflection-1.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
934
docs/classes/reflection-560.reflection-1.sasjs.html
Normal file
934
docs/classes/reflection-560.reflection-1.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
892
docs/classes/reflection-560.reflection-1.sasviyaapiclient.html
Normal file
892
docs/classes/reflection-560.reflection-1.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
206
docs/classes/reflection-560.reflection-1.sessionmanager.html
Normal file
206
docs/classes/reflection-560.reflection-1.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
294
docs/classes/reflection-588.reflection-1.sas9apiclient.html
Normal file
294
docs/classes/reflection-588.reflection-1.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1017
docs/classes/reflection-588.reflection-1.sasjs.html
Normal file
1017
docs/classes/reflection-588.reflection-1.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
913
docs/classes/reflection-588.reflection-1.sasviyaapiclient.html
Normal file
913
docs/classes/reflection-588.reflection-1.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
227
docs/classes/reflection-588.reflection-1.sessionmanager.html
Normal file
227
docs/classes/reflection-588.reflection-1.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
224
docs/classes/reflection-607.reflection-145.fileuploader.html
Normal file
224
docs/classes/reflection-607.reflection-145.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
297
docs/classes/reflection-607.reflection-145.sas9apiclient.html
Normal file
297
docs/classes/reflection-607.reflection-145.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1021
docs/classes/reflection-607.reflection-145.sasjs.html
Normal file
1021
docs/classes/reflection-607.reflection-145.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
916
docs/classes/reflection-607.reflection-145.sasviyaapiclient.html
Normal file
916
docs/classes/reflection-607.reflection-145.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
230
docs/classes/reflection-607.reflection-145.sessionmanager.html
Normal file
230
docs/classes/reflection-607.reflection-145.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
221
docs/classes/reflection-610.reflection-149.fileuploader.html
Normal file
221
docs/classes/reflection-610.reflection-149.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
297
docs/classes/reflection-610.reflection-149.sas9apiclient.html
Normal file
297
docs/classes/reflection-610.reflection-149.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1015
docs/classes/reflection-610.reflection-149.sasjs.html
Normal file
1015
docs/classes/reflection-610.reflection-149.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
916
docs/classes/reflection-610.reflection-149.sasviyaapiclient.html
Normal file
916
docs/classes/reflection-610.reflection-149.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
230
docs/classes/reflection-610.reflection-149.sessionmanager.html
Normal file
230
docs/classes/reflection-610.reflection-149.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
221
docs/classes/reflection-611.reflection-149.fileuploader.html
Normal file
221
docs/classes/reflection-611.reflection-149.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
297
docs/classes/reflection-611.reflection-149.sas9apiclient.html
Normal file
297
docs/classes/reflection-611.reflection-149.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1012
docs/classes/reflection-611.reflection-149.sasjs.html
Normal file
1012
docs/classes/reflection-611.reflection-149.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
916
docs/classes/reflection-611.reflection-149.sasviyaapiclient.html
Normal file
916
docs/classes/reflection-611.reflection-149.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
230
docs/classes/reflection-611.reflection-149.sessionmanager.html
Normal file
230
docs/classes/reflection-611.reflection-149.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
198
docs/index.html
198
docs/index.html
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
179
docs/interfaces/types.jobdefinition.html
Normal file
179
docs/interfaces/types.jobdefinition.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
198
docs/interfaces/types.uploadfile.html
Normal file
198
docs/interfaces/types.uploadfile.html
Normal file
File diff suppressed because one or more lines are too long
107
docs/modules/reflection-560.html
Normal file
107
docs/modules/reflection-560.html
Normal file
File diff suppressed because one or more lines are too long
125
docs/modules/reflection-560.reflection-1.html
Normal file
125
docs/modules/reflection-560.reflection-1.html
Normal file
File diff suppressed because one or more lines are too long
107
docs/modules/reflection-588.html
Normal file
107
docs/modules/reflection-588.html
Normal file
File diff suppressed because one or more lines are too long
125
docs/modules/reflection-588.reflection-1.html
Normal file
125
docs/modules/reflection-588.reflection-1.html
Normal file
File diff suppressed because one or more lines are too long
107
docs/modules/reflection-607.html
Normal file
107
docs/modules/reflection-607.html
Normal file
File diff suppressed because one or more lines are too long
129
docs/modules/reflection-607.reflection-145.html
Normal file
129
docs/modules/reflection-607.reflection-145.html
Normal file
File diff suppressed because one or more lines are too long
107
docs/modules/reflection-610.html
Normal file
107
docs/modules/reflection-610.html
Normal file
File diff suppressed because one or more lines are too long
129
docs/modules/reflection-610.reflection-149.html
Normal file
129
docs/modules/reflection-610.reflection-149.html
Normal file
File diff suppressed because one or more lines are too long
107
docs/modules/reflection-611.html
Normal file
107
docs/modules/reflection-611.html
Normal file
File diff suppressed because one or more lines are too long
129
docs/modules/reflection-611.reflection-149.html
Normal file
129
docs/modules/reflection-611.reflection-149.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
example.html
10
example.html
@@ -1,12 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1"></script>
|
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1"></script>
|
||||||
<script>
|
<script>
|
||||||
var sasJs = new SASjs.default({appLoc: "/Products/demo/readme"
|
var sasJs = new SASjs.default({
|
||||||
,serverType:"SAS9", debug: "false"
|
appLoc: "/Public/app/readme"
|
||||||
|
,serverType:"SAS9"
|
||||||
|
,debug: false
|
||||||
});
|
});
|
||||||
function initSasJs() {
|
function initSasJs() {
|
||||||
$('#loading-spinner').show()
|
$('#loading-spinner').show()
|
||||||
@@ -70,6 +70,8 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid" style="text-align: center; margin-top: 10px;">
|
<div class="container-fluid" style="text-align: center; margin-top: 10px;">
|
||||||
|
|||||||
1721
package-lock.json
generated
1721
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -5,8 +5,8 @@
|
|||||||
"build": "rimraf build && webpack",
|
"build": "rimraf build && webpack",
|
||||||
"package:lib": "npm run build && cp ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
|
"package:lib": "npm run build && cp ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
|
||||||
"publish:lib": "npm run build && cd build && npm publish",
|
"publish:lib": "npm run build && cd build && npm publish",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
|
"lint:fix": "npx prettier --write 'src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
|
||||||
"lint": "tslint -p tsconfig.json",
|
"lint": "npx prettier --check 'src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
|
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
|
||||||
"postpublish": "git clean -fd",
|
"postpublish": "git clean -fd",
|
||||||
@@ -22,9 +22,6 @@
|
|||||||
{
|
{
|
||||||
"pkgRoot": "/build"
|
"pkgRoot": "/build"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"branches": [
|
|
||||||
"main"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -40,23 +37,22 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/isomorphic-fetch": "0.0.35",
|
"@types/isomorphic-fetch": "0.0.35",
|
||||||
"@types/jest": "^26.0.4",
|
"@types/jest": "^26.0.10",
|
||||||
"cp": "^0.2.0",
|
"cp": "^0.2.0",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prettier": "^2.0.5",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"semantic-release": "^17.1.1",
|
"semantic-release": "^17.1.1",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
"ts-loader": "^7.0.5",
|
"ts-loader": "^8.0.3",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.3",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"typedoc": "^0.17.8",
|
"typedoc": "^0.17.8",
|
||||||
"typedoc-neo-theme": "^1.0.9",
|
"typedoc-neo-theme": "^1.0.10",
|
||||||
"typedoc-plugin-external-module-name": "^4.0.3",
|
"typedoc-plugin-external-module-name": "^4.0.3",
|
||||||
"typescript": "^3.9.6",
|
"typescript": "^3.9.7",
|
||||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.44.1",
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
6
sasjs-tests/.prettierrc
Normal file
6
sasjs-tests/.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
||||||
252
sasjs-tests/package-lock.json
generated
252
sasjs-tests/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sasjs-tests",
|
"name": "@sasjs/tests",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1356,11 +1356,81 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
|
"@sasjs/adapter": {
|
||||||
|
"version": "1.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.6.tgz",
|
||||||
|
"integrity": "sha512-d2B+cTII+vabKCU8mJy90mEz3tCWw2pEp4qIBGsDamJiTS0Rx69dgXGHuRUm8KtjLDHHrzwXATsqviU3dnU0QQ==",
|
||||||
|
"requires": {
|
||||||
|
"es6-promise": "^4.2.8",
|
||||||
|
"form-data": "^3.0.0",
|
||||||
|
"isomorphic-fetch": "^2.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@sasjs/test-framework": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-Pd8PUH5B5RO6q4w3OQXX7aWicvA/CJMXA/FCf2xp332ZTKBb/5uV+HphAOFKpCh58y+ykYYVSV0ZaDO/4t1h3A==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react-highlight.js": "^1.0.0",
|
||||||
|
"immer": "^7.0.7",
|
||||||
|
"moment": "^2.27.0",
|
||||||
|
"react-highlight.js": "^1.0.7",
|
||||||
|
"semantic-ui-css": "^2.4.1",
|
||||||
|
"semantic-ui-react": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"immer": {
|
||||||
|
"version": "7.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.7.tgz",
|
||||||
|
"integrity": "sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@semantic-ui-react/event-stack": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-SA7VOu/tY3OkooR++mm9voeQrJpYXjJaMHO1aFCcSouS2xhqMR9Gnz0LEGLOR0h9ueWPBKaQzKIrx3FTTJZmUQ==",
|
||||||
|
"requires": {
|
||||||
|
"exenv": "^1.2.2",
|
||||||
|
"prop-types": "^15.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@sheerun/mutationobserver-shim": {
|
"@sheerun/mutationobserver-shim": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
||||||
"integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw=="
|
"integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw=="
|
||||||
},
|
},
|
||||||
|
"@stardust-ui/react-component-event-listener": {
|
||||||
|
"version": "0.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stardust-ui/react-component-event-listener/-/react-component-event-listener-0.38.0.tgz",
|
||||||
|
"integrity": "sha512-sIP/e0dyOrrlb8K7KWumfMxj/gAifswTBC4o68Aa+C/GA73ccRp/6W1VlHvF/dlOR4KLsA+5SKnhjH36xzPsWg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@stardust-ui/react-component-ref": {
|
||||||
|
"version": "0.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stardust-ui/react-component-ref/-/react-component-ref-0.38.0.tgz",
|
||||||
|
"integrity": "sha512-xjs6WnvJVueSIXMWw0C3oWIgAPpcD03qw43oGOjUXqFktvpNkB73JoKIhS4sCrtQxBdct75qqr4ZL6JiyPcESw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@svgr/babel-plugin-add-jsx-attribute": {
|
"@svgr/babel-plugin-add-jsx-attribute": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
|
||||||
@@ -1836,6 +1906,14 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-highlight.js": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-highlight.js/-/react-highlight.js-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5VXEuo2O9L66y/2GDQSGFTggQkpOvDc/p2ma1KHadu7o/H720HK3Fr83epd4wtQky7B/RoCPat0SKyhlhiUo7A==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-router": {
|
"@types/react-router": {
|
||||||
"version": "5.1.8",
|
"version": "5.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
|
||||||
@@ -3754,6 +3832,11 @@
|
|||||||
"shallow-clone": "^0.1.2"
|
"shallow-clone": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clsx": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
|
||||||
|
},
|
||||||
"co": {
|
"co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -4095,6 +4178,15 @@
|
|||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"create-react-context": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==",
|
||||||
|
"requires": {
|
||||||
|
"gud": "^1.0.0",
|
||||||
|
"warning": "^4.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "6.0.5",
|
"version": "6.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||||
@@ -4912,11 +5004,21 @@
|
|||||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
},
|
},
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||||
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
|
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"iconv-lite": "~0.4.13"
|
"iconv-lite": "^0.6.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"end-of-stream": {
|
"end-of-stream": {
|
||||||
@@ -5609,6 +5711,11 @@
|
|||||||
"strip-eof": "^1.0.0"
|
"strip-eof": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exenv": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
|
||||||
|
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
|
||||||
|
},
|
||||||
"exit": {
|
"exit": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
||||||
@@ -6478,6 +6585,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
||||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||||
},
|
},
|
||||||
|
"gud": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||||
|
},
|
||||||
"gzip-size": {
|
"gzip-size": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
||||||
@@ -6622,6 +6734,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||||
},
|
},
|
||||||
|
"highlight.js": {
|
||||||
|
"version": "9.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz",
|
||||||
|
"integrity": "sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ=="
|
||||||
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"version": "4.10.1",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||||
@@ -7974,6 +8091,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jquery": {
|
||||||
|
"version": "3.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
|
||||||
|
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
|
||||||
|
},
|
||||||
"js-base64": {
|
"js-base64": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz",
|
||||||
@@ -8123,6 +8245,11 @@
|
|||||||
"object.assign": "^4.1.0"
|
"object.assign": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"keyboard-key": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ=="
|
||||||
|
},
|
||||||
"killable": {
|
"killable": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||||
@@ -8887,6 +9014,11 @@
|
|||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||||
|
},
|
||||||
"move-concurrently": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||||
@@ -9880,6 +10012,11 @@
|
|||||||
"ts-pnp": "^1.1.6"
|
"ts-pnp": "^1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"popper.js": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
|
||||||
|
},
|
||||||
"portfinder": {
|
"portfinder": {
|
||||||
"version": "1.0.26",
|
"version": "1.0.26",
|
||||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
|
||||||
@@ -11321,11 +11458,34 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||||
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
|
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
|
||||||
},
|
},
|
||||||
|
"react-highlight.js": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-highlight.js/-/react-highlight.js-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-OVPKnV0ZvU+V//HExwbV8M9CWy49Eo/9y9pBN2OsNWUFPN6dE4YZBLmJW/5sM2DxI5v/QQLyxOnTnSSfGCP+9Q==",
|
||||||
|
"requires": {
|
||||||
|
"highlight.js": "^9.3.0",
|
||||||
|
"prop-types": "^15.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"react-popper": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"create-react-context": "^0.3.0",
|
||||||
|
"deep-equal": "^1.1.1",
|
||||||
|
"popper.js": "^1.14.4",
|
||||||
|
"prop-types": "^15.6.1",
|
||||||
|
"typed-styles": "^0.0.7",
|
||||||
|
"warning": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||||
@@ -11950,27 +12110,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz",
|
||||||
"integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg=="
|
"integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg=="
|
||||||
},
|
},
|
||||||
"sasjs": {
|
|
||||||
"version": "file:../build/sasjs-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-8Ez2iS8BKzu2GG1Cwf/pe5PgNvdhowFodQNCTHIxMlDYgLqmg1mcpwRjJjnXF9A73gX0NkR65olYYAesp8cMMA==",
|
|
||||||
"requires": {
|
|
||||||
"es6-promise": "^4.2.8",
|
|
||||||
"form-data": "^3.0.0",
|
|
||||||
"isomorphic-fetch": "^2.2.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"form-data": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
|
||||||
"requires": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sass-graph": {
|
"sass-graph": {
|
||||||
"version": "2.2.5",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
|
||||||
@@ -12086,6 +12225,47 @@
|
|||||||
"node-forge": "0.9.0"
|
"node-forge": "0.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"semantic-ui-css": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semantic-ui-css/-/semantic-ui-css-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-Pkp0p9oWOxlH0kODx7qFpIRYpK1T4WJOO4lNnpNPOoWKCrYsfHqYSKgk5fHfQtnWnsAKy7nLJMW02bgDWWFZFg==",
|
||||||
|
"requires": {
|
||||||
|
"jquery": "x.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"semantic-ui-react": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9tNL94nEy16RdupTQNiURyemWUIxtTpQgFimCbOOHRBOe1ApsFz3FWFsrGjv9zFtE7dQMslLYov9BQOelTCVwA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.10.5",
|
||||||
|
"@semantic-ui-react/event-stack": "^3.1.0",
|
||||||
|
"@stardust-ui/react-component-event-listener": "~0.38.0",
|
||||||
|
"@stardust-ui/react-component-ref": "~0.38.0",
|
||||||
|
"clsx": "^1.1.1",
|
||||||
|
"keyboard-key": "^1.1.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.6",
|
||||||
|
"react-popper": "^1.3.7",
|
||||||
|
"shallowequal": "^1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": {
|
||||||
|
"version": "7.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
|
||||||
|
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
|
||||||
|
"requires": {
|
||||||
|
"regenerator-runtime": "^0.13.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||||
|
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
@@ -12275,6 +12455,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"shallowequal": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||||
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||||
@@ -13443,6 +13628,11 @@
|
|||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"typed-styles": {
|
||||||
|
"version": "0.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
|
||||||
|
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
|
||||||
|
},
|
||||||
"typedarray": {
|
"typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
@@ -13744,6 +13934,14 @@
|
|||||||
"makeerror": "1.0.x"
|
"makeerror": "1.0.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"warning": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "sasjs-tests",
|
"name": "@sasjs/tests",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"homepage": ".",
|
"homepage": ".",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sasjs/adapter": "^1.3.6",
|
||||||
|
"@sasjs/test-framework": "^1.4.0",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.5.0",
|
"@testing-library/react": "^9.5.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
@@ -16,7 +18,6 @@
|
|||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"sasjs": "file:../build/sasjs-5.0.0.tgz",
|
|
||||||
"typescript": "^3.9.6"
|
"typescript": "^3.9.6"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"deploy": "rsync -avhe ssh ./build/* --delete kriaco@sas.analytium.co.uk:/var/www/html/kriaco/sasjs-tests"
|
"deploy": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz && npm run build && rsync -avhe ssh ./build/* --delete kriaco@sas.analytium.co.uk:/var/www/html/kriaco/sasjs-tests"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"appLoc": "/Public/app",
|
"appLoc": "/Public/app",
|
||||||
"serverType": "SASVIYA",
|
"serverType": "SASVIYA",
|
||||||
"debug": false,
|
"debug": false,
|
||||||
"contextName": null
|
"contextName": "SharedCompute",
|
||||||
|
"useComputeApi": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
.app {
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.debug-toggle,
|
|
||||||
.app-loc-input,
|
|
||||||
.submit-button {
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
margin: 16px;
|
|
||||||
|
|
||||||
&.app-loc {
|
|
||||||
width: 20vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button {
|
|
||||||
padding: 16px;
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-loc-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.debug-toggle {
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 0 8px;
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$height: 40px;
|
|
||||||
$width: 70px;
|
|
||||||
.switch {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
width: $width;
|
|
||||||
height: $height;
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
input:checked + .knob {
|
|
||||||
animation: colorChange 0.4s linear forwards;
|
|
||||||
}
|
|
||||||
input:checked + .knob:before {
|
|
||||||
animation: turnON 0.4s linear forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes colorChange {
|
|
||||||
from {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: #a4d9ad;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
background-color: #4bd663;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes turnON {
|
|
||||||
from {
|
|
||||||
transform: translateX(0px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateX($width - ($height * 0.99));
|
|
||||||
box-shadow: -10px 0px 44px 0px #434343;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.knob {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #ccc;
|
|
||||||
border-radius: $height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knob:before {
|
|
||||||
position: absolute;
|
|
||||||
background-color: white;
|
|
||||||
content: "";
|
|
||||||
left: $height * 0.1;
|
|
||||||
top: $height * 0.1;
|
|
||||||
width: ($height * 0.8);
|
|
||||||
height: ($height * 0.8);
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { render } from '@testing-library/react';
|
import { render } from "@testing-library/react";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
test("renders learn react link", () => {
|
||||||
const { getByText } = render(<App />);
|
const { getByText } = render(<App />);
|
||||||
const linkElement = getByText(/learn react/i);
|
const linkElement = getByText(/learn react/i);
|
||||||
expect(linkElement).toBeInTheDocument();
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
|||||||
@@ -1,56 +1,30 @@
|
|||||||
import React, { ReactElement, useState, useContext, useEffect } from "react";
|
import React, { ReactElement, useState, useContext, useEffect } from "react";
|
||||||
import "./App.scss";
|
import { TestSuiteRunner, TestSuite, AppContext } from "@sasjs/test-framework";
|
||||||
import TestSuiteRunner from "./TestSuiteRunner";
|
import { basicTests } from "./testSuites/Basic";
|
||||||
import { AppContext } from "./context/AppContext";
|
import { sendArrTests, sendObjTests } from "./testSuites/RequestData";
|
||||||
|
import { specialCaseTests } from "./testSuites/SpecialCases";
|
||||||
|
import { sasjsRequestTests } from "./testSuites/SasjsRequests";
|
||||||
|
import "@sasjs/test-framework/dist/index.css";
|
||||||
|
|
||||||
const App = (): ReactElement<{}> => {
|
const App = (): ReactElement<{}> => {
|
||||||
const [appLoc, setAppLoc] = useState("");
|
const { adapter, config } = useContext(AppContext);
|
||||||
const [debug, setDebug] = useState(false);
|
const [testSuites, setTestSuites] = useState<TestSuite[]>([]);
|
||||||
const { adapter } = useContext(AppContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (adapter) {
|
if (adapter) {
|
||||||
adapter.setDebugState(debug);
|
setTestSuites([
|
||||||
|
basicTests(adapter, config.userName, config.password),
|
||||||
|
sendArrTests(adapter),
|
||||||
|
sendObjTests(adapter),
|
||||||
|
specialCaseTests(adapter),
|
||||||
|
sasjsRequestTests(adapter)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}, [debug, adapter]);
|
}, [adapter, config]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (appLoc && adapter) {
|
|
||||||
adapter.setSASjsConfig({ ...adapter.getSasjsConfig(), appLoc });
|
|
||||||
}
|
|
||||||
}, [appLoc, adapter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAppLoc(adapter.getSasjsConfig().appLoc);
|
|
||||||
}, [adapter]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<div className="controls">
|
{adapter && testSuites && <TestSuiteRunner testSuites={testSuites} />}
|
||||||
<div className="row">
|
|
||||||
<label>Debug</label>
|
|
||||||
<div className="debug-toggle">
|
|
||||||
<label className="switch">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
onChange={(e) => setDebug(e.target.checked)}
|
|
||||||
/>
|
|
||||||
<span className="knob"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="row app-loc">
|
|
||||||
<label>App Loc</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="app-loc-input"
|
|
||||||
value={appLoc}
|
|
||||||
onChange={(e) => setAppLoc(e.target.value)}
|
|
||||||
placeholder="AppLoc"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{adapter && <TestSuiteRunner adapter={adapter} />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement, useState, useCallback, useContext } from "react";
|
import React, { ReactElement, useState, useCallback, useContext } from "react";
|
||||||
import "./Login.scss";
|
import "./Login.scss";
|
||||||
import { AppContext } from "./context/AppContext";
|
import { AppContext } from "@sasjs/test-framework";
|
||||||
import { Redirect } from "react-router-dom";
|
import { Redirect } from "react-router-dom";
|
||||||
|
|
||||||
const Login = (): ReactElement<{}> => {
|
const Login = (): ReactElement<{}> => {
|
||||||
@@ -11,8 +11,8 @@ const Login = (): ReactElement<{}> => {
|
|||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
appContext.adapter.logIn(username, password).then(() => {
|
appContext.adapter.logIn(username, password).then((res) => {
|
||||||
appContext.setIsLoggedIn(true);
|
appContext.setIsLoggedIn(res.isLoggedIn);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[username, password, appContext]
|
[username, password, appContext]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement, useContext, FunctionComponent } from "react";
|
import React, { ReactElement, useContext, FunctionComponent } from "react";
|
||||||
import { Redirect, Route } from "react-router-dom";
|
import { Redirect, Route } from "react-router-dom";
|
||||||
import { AppContext } from "./context/AppContext";
|
import { AppContext } from "@sasjs/test-framework";
|
||||||
|
|
||||||
interface PrivateRouteProps {
|
interface PrivateRouteProps {
|
||||||
component: FunctionComponent;
|
component: FunctionComponent;
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button {
|
|
||||||
padding: 10px;
|
|
||||||
min-height: 80px;
|
|
||||||
font-size: 2em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import React, { useEffect, useState, ReactElement, useContext } from "react";
|
|
||||||
import TestSuiteComponent from "./components/TestSuite";
|
|
||||||
import TestSuiteCard from "./components/TestSuiteCard";
|
|
||||||
import { TestSuite, Test } from "./types";
|
|
||||||
import { basicTests } from "./testSuites/Basic";
|
|
||||||
import "./TestSuiteRunner.scss";
|
|
||||||
import SASjs from "sasjs";
|
|
||||||
import { AppContext } from "./context/AppContext";
|
|
||||||
import { sendArrTests, sendObjTests } from "./testSuites/RequestData";
|
|
||||||
import { specialCaseTests } from "./testSuites/SpecialCases";
|
|
||||||
import { sasjsRequestTests } from "./testSuites/SasjsRequests";
|
|
||||||
|
|
||||||
interface TestSuiteRunnerProps {
|
|
||||||
adapter: SASjs;
|
|
||||||
}
|
|
||||||
const TestSuiteRunner = (
|
|
||||||
props: TestSuiteRunnerProps
|
|
||||||
): ReactElement<TestSuiteRunnerProps> => {
|
|
||||||
const { adapter } = props;
|
|
||||||
const { config } = useContext(AppContext);
|
|
||||||
const [testSuites, setTestSuites] = useState<TestSuite[]>([]);
|
|
||||||
const [runTests, setRunTests] = useState(false);
|
|
||||||
const [completedTestSuites, setCompletedTestSuites] = useState<
|
|
||||||
{
|
|
||||||
name: string;
|
|
||||||
completedTests: {
|
|
||||||
test: Test;
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}[];
|
|
||||||
}[]
|
|
||||||
>([]);
|
|
||||||
const [currentTestSuite, setCurrentTestSuite] = useState<TestSuite | null>(
|
|
||||||
(null as unknown) as TestSuite
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (adapter) {
|
|
||||||
setTestSuites([
|
|
||||||
// basicTests(adapter, config.userName, config.password),
|
|
||||||
// sendArrTests(adapter),
|
|
||||||
// sendObjTests(adapter),
|
|
||||||
specialCaseTests(adapter),
|
|
||||||
// sasjsRequestTests(adapter),
|
|
||||||
]);
|
|
||||||
setCompletedTestSuites([]);
|
|
||||||
}
|
|
||||||
}, [adapter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (testSuites.length) {
|
|
||||||
setCurrentTestSuite(testSuites[0]);
|
|
||||||
}
|
|
||||||
}, [testSuites]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (runTests) {
|
|
||||||
setCompletedTestSuites([]);
|
|
||||||
setCurrentTestSuite(testSuites[0]);
|
|
||||||
}
|
|
||||||
}, [runTests, testSuites]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="button-container">
|
|
||||||
<button
|
|
||||||
className={runTests ? "submit-button disabled" : "submit-button"}
|
|
||||||
onClick={() => setRunTests(true)}
|
|
||||||
disabled={runTests}
|
|
||||||
>
|
|
||||||
{runTests ? (
|
|
||||||
<>
|
|
||||||
<div className="loading-spinner"></div>Running tests...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"Run tests!"
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{completedTestSuites.map((completedTestSuite, index) => {
|
|
||||||
return (
|
|
||||||
<TestSuiteCard
|
|
||||||
key={index}
|
|
||||||
tests={completedTestSuite.completedTests}
|
|
||||||
name={completedTestSuite.name}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{currentTestSuite && runTests && (
|
|
||||||
<TestSuiteComponent
|
|
||||||
{...currentTestSuite}
|
|
||||||
onCompleted={(
|
|
||||||
name,
|
|
||||||
completedTests: {
|
|
||||||
test: Test;
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}[]
|
|
||||||
) => {
|
|
||||||
const currentIndex = testSuites.indexOf(currentTestSuite);
|
|
||||||
const nextIndex =
|
|
||||||
currentIndex < testSuites.length - 1 ? currentIndex + 1 : -1;
|
|
||||||
if (nextIndex >= 0) {
|
|
||||||
setCurrentTestSuite(testSuites[nextIndex]);
|
|
||||||
} else {
|
|
||||||
setCurrentTestSuite(null);
|
|
||||||
}
|
|
||||||
const newCompletedTestSuites = [
|
|
||||||
...completedTestSuites,
|
|
||||||
{ name, completedTests },
|
|
||||||
];
|
|
||||||
setCompletedTestSuites(newCompletedTestSuites);
|
|
||||||
|
|
||||||
if (newCompletedTestSuites.length === testSuites.length) {
|
|
||||||
setRunTests(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestSuiteRunner;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from "react";
|
|
||||||
import TestCard from "./TestCard";
|
|
||||||
import { start } from "repl";
|
|
||||||
|
|
||||||
interface TestProps {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
beforeTest?: (...args: any) => Promise<any>;
|
|
||||||
afterTest?: (...args: any) => Promise<any>;
|
|
||||||
test: (context: any) => Promise<any>;
|
|
||||||
assertion: (...args: any) => boolean;
|
|
||||||
onCompleted: (payload: {
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}) => void;
|
|
||||||
context: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getStatus = (isRunning: boolean, isPassed: boolean): string => {
|
|
||||||
return isRunning ? "running" : isPassed ? "passed" : "failed";
|
|
||||||
};
|
|
||||||
|
|
||||||
const Test = (props: TestProps): ReactElement<TestProps> => {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
test,
|
|
||||||
beforeTest,
|
|
||||||
afterTest,
|
|
||||||
assertion,
|
|
||||||
onCompleted,
|
|
||||||
context,
|
|
||||||
} = props;
|
|
||||||
const beforeTestFunction = beforeTest ? beforeTest : () => Promise.resolve();
|
|
||||||
const afterTestFunction = afterTest ? afterTest : () => Promise.resolve();
|
|
||||||
const [isRunning, setIsRunning] = useState(false);
|
|
||||||
const [isPassed, setIsPassed] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (test && assertion) {
|
|
||||||
const startTime = new Date().valueOf();
|
|
||||||
setIsRunning(true);
|
|
||||||
setIsPassed(false);
|
|
||||||
beforeTestFunction()
|
|
||||||
.then(() => test(context))
|
|
||||||
.then((res) => {
|
|
||||||
setIsRunning(false);
|
|
||||||
setIsPassed(assertion(res, context));
|
|
||||||
return Promise.resolve(assertion(res, context));
|
|
||||||
})
|
|
||||||
.then((testResult) => {
|
|
||||||
afterTestFunction();
|
|
||||||
const endTime = new Date().valueOf();
|
|
||||||
const executionTime = (endTime - startTime) / 1000;
|
|
||||||
onCompleted({ result: testResult, error: null, executionTime });
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
setIsRunning(false);
|
|
||||||
setIsPassed(false);
|
|
||||||
console.error(e);
|
|
||||||
const endTime = new Date().valueOf();
|
|
||||||
const executionTime = (endTime - startTime) / 1000;
|
|
||||||
onCompleted({ result: false, error: e, executionTime });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [test, assertion]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TestCard
|
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
status={getStatus(isRunning, isPassed)}
|
|
||||||
error={null}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Test;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
.test {
|
|
||||||
display: inline-flex;
|
|
||||||
padding: 8px;
|
|
||||||
margin: 8px;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: 20%;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #eee;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description,
|
|
||||||
.execution-time {
|
|
||||||
color: #c6c0c0;
|
|
||||||
padding: 8px 0;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
min-height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.execution-time {
|
|
||||||
color: #f9e804;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
margin-right: 8px;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
&.running {
|
|
||||||
background-color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.passed {
|
|
||||||
background-color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.failed {
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 900px) {
|
|
||||||
.test {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 901px) and (max-width: 1280px) {
|
|
||||||
.test {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React, { ReactElement } from "react";
|
|
||||||
import "./TestCard.scss";
|
|
||||||
|
|
||||||
interface TestCardProps {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
status: string;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime?: number;
|
|
||||||
}
|
|
||||||
const TestCard = (props: TestCardProps): ReactElement<TestCardProps> => {
|
|
||||||
const { title, description, status, error, executionTime } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="test">
|
|
||||||
<code className="title">{title}</code>
|
|
||||||
<span className="description">{description}</span>
|
|
||||||
<span className="execution-time">
|
|
||||||
{executionTime ? executionTime.toFixed(2) + "s" : ""}
|
|
||||||
</span>
|
|
||||||
{status === "running" && (
|
|
||||||
<div>
|
|
||||||
<span className="icon running"></span>Running...
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{status === "passed" && (
|
|
||||||
<div>
|
|
||||||
<span className="icon passed"></span>Passed
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{status === "failed" && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<span className="icon failed"></span>Failed
|
|
||||||
</div>
|
|
||||||
{!!error && <code>{error.message}</code>}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestCard;
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from "react";
|
|
||||||
import "./TestSuiteCard.scss";
|
|
||||||
import { Test } from "../types";
|
|
||||||
import TestComponent from "./Test";
|
|
||||||
import TestCard from "./TestCard";
|
|
||||||
|
|
||||||
interface TestSuiteProps {
|
|
||||||
name: string;
|
|
||||||
tests: Test[];
|
|
||||||
beforeAll?: (...args: any) => Promise<any>;
|
|
||||||
afterAll?: (...args: any) => Promise<any>;
|
|
||||||
onCompleted: (
|
|
||||||
name: string,
|
|
||||||
completedTests: {
|
|
||||||
test: Test;
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}[]
|
|
||||||
) => void;
|
|
||||||
}
|
|
||||||
const TestSuite = (props: TestSuiteProps): ReactElement<TestSuiteProps> => {
|
|
||||||
const { name, tests, beforeAll, afterAll, onCompleted } = props;
|
|
||||||
const [context, setContext] = useState<any>(null);
|
|
||||||
const [completedTests, setCompletedTests] = useState<
|
|
||||||
{
|
|
||||||
test: Test;
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}[]
|
|
||||||
>([]);
|
|
||||||
const [currentTest, setCurrentTest] = useState<Test | null>(
|
|
||||||
(null as unknown) as Test
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (beforeAll) {
|
|
||||||
beforeAll().then((data) => setContext({ data }));
|
|
||||||
}
|
|
||||||
}, [beforeAll]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (tests.length) {
|
|
||||||
setCurrentTest(tests[0]);
|
|
||||||
}
|
|
||||||
setCompletedTests([]);
|
|
||||||
setContext(null);
|
|
||||||
}, [tests]);
|
|
||||||
|
|
||||||
return (!!beforeAll && !!context) || !beforeAll ? (
|
|
||||||
<div className="test-suite">
|
|
||||||
<div className="test-suite-name running">{name}</div>
|
|
||||||
{currentTest && (
|
|
||||||
<TestComponent
|
|
||||||
{...currentTest}
|
|
||||||
context={context}
|
|
||||||
onCompleted={(completedTest) => {
|
|
||||||
const newCompleteTests = [
|
|
||||||
...completedTests,
|
|
||||||
{
|
|
||||||
test: currentTest,
|
|
||||||
result: completedTest.result,
|
|
||||||
error: completedTest.error,
|
|
||||||
executionTime: completedTest.executionTime,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
setCompletedTests(newCompleteTests);
|
|
||||||
const currentIndex = tests.indexOf(currentTest);
|
|
||||||
const nextIndex =
|
|
||||||
currentIndex < tests.length - 1 ? currentIndex + 1 : -1;
|
|
||||||
if (nextIndex >= 0) {
|
|
||||||
setCurrentTest(tests[nextIndex]);
|
|
||||||
} else {
|
|
||||||
setCurrentTest(null);
|
|
||||||
}
|
|
||||||
if (newCompleteTests.length === tests.length) {
|
|
||||||
if (afterAll) {
|
|
||||||
afterAll().then(() => onCompleted(name, newCompleteTests));
|
|
||||||
} else {
|
|
||||||
onCompleted(name, newCompleteTests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{completedTests.map((completedTest, index) => {
|
|
||||||
const { test, result, error } = completedTest;
|
|
||||||
const { title, description } = test;
|
|
||||||
return (
|
|
||||||
<TestCard
|
|
||||||
key={index}
|
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
status={result === true ? "passed" : "failed"}
|
|
||||||
error={error}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestSuite;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
.test-suite {
|
|
||||||
.test-suite-name {
|
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #1f2027;
|
|
||||||
|
|
||||||
&.passed {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.failed {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.running {
|
|
||||||
color: yellow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import React, { ReactElement } from "react";
|
|
||||||
import "./TestSuiteCard.scss";
|
|
||||||
import { Test } from "../types";
|
|
||||||
import TestCard from "./TestCard";
|
|
||||||
|
|
||||||
interface TestSuiteCardProps {
|
|
||||||
name: string;
|
|
||||||
tests: {
|
|
||||||
test: Test;
|
|
||||||
result: boolean;
|
|
||||||
error: Error | null;
|
|
||||||
executionTime: number;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
const TestSuiteCard = (
|
|
||||||
props: TestSuiteCardProps
|
|
||||||
): ReactElement<TestSuiteCardProps> => {
|
|
||||||
const { name, tests } = props;
|
|
||||||
const overallStatus = tests.map((t) => t.result).reduce((x, y) => x && y);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="test-suite">
|
|
||||||
<div className={`test-suite-name ${overallStatus ? "passed" : "failed"}`}>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
{tests.map((completedTest, index) => {
|
|
||||||
const { test, result, error, executionTime } = completedTest;
|
|
||||||
const { title, description } = test;
|
|
||||||
return (
|
|
||||||
<TestCard
|
|
||||||
key={index}
|
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
status={result === true ? "passed" : "failed"}
|
|
||||||
error={error}
|
|
||||||
executionTime={executionTime}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestSuiteCard;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import React, { createContext, useState, useEffect, ReactNode } from "react";
|
|
||||||
import SASjs from "sasjs";
|
|
||||||
|
|
||||||
export const AppContext = createContext<{
|
|
||||||
config: any;
|
|
||||||
sasJsConfig: any;
|
|
||||||
isLoggedIn: boolean;
|
|
||||||
setIsLoggedIn: (value: boolean) => void;
|
|
||||||
adapter: SASjs;
|
|
||||||
}>({
|
|
||||||
config: null,
|
|
||||||
sasJsConfig: null,
|
|
||||||
isLoggedIn: false,
|
|
||||||
setIsLoggedIn: (null as unknown) as (value: boolean) => void,
|
|
||||||
adapter: (null as unknown) as SASjs,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const AppProvider = (props: { children: ReactNode }) => {
|
|
||||||
const [config, setConfig] = useState<{ sasJsConfig: any }>({
|
|
||||||
sasJsConfig: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [adapter, setAdapter] = useState<SASjs>((null as unknown) as SASjs);
|
|
||||||
|
|
||||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetch("config.json")
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((configJson: any) => {
|
|
||||||
setConfig(configJson);
|
|
||||||
const sasjs = new SASjs(configJson.sasJsConfig);
|
|
||||||
setAdapter(sasjs);
|
|
||||||
sasjs.checkSession().then((response) => {
|
|
||||||
setIsLoggedIn(response.isLoggedIn);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppContext.Provider
|
|
||||||
value={{
|
|
||||||
config,
|
|
||||||
sasJsConfig: config.sasJsConfig,
|
|
||||||
isLoggedIn,
|
|
||||||
setIsLoggedIn,
|
|
||||||
adapter,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</AppContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
|
||||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
"Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
background-color: #1f2027;
|
background-color: #1f2027;
|
||||||
@@ -10,8 +9,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
|
|||||||
import { Route, HashRouter, Switch } from "react-router-dom";
|
import { Route, HashRouter, Switch } from "react-router-dom";
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import * as serviceWorker from "./serviceWorker";
|
import * as serviceWorker from "./serviceWorker";
|
||||||
import { AppProvider } from "./context/AppContext";
|
import { AppProvider } from "@sasjs/test-framework";
|
||||||
import PrivateRoute from "./PrivateRoute";
|
import PrivateRoute from "./PrivateRoute";
|
||||||
import Login from "./Login";
|
import Login from "./Login";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
// opt-in, read https://bit.ly/CRA-PWA
|
// opt-in, read https://bit.ly/CRA-PWA
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
const isLocalhost = Boolean(
|
||||||
window.location.hostname === 'localhost' ||
|
window.location.hostname === "localhost" ||
|
||||||
// [::1] is the IPv6 localhost address.
|
// [::1] is the IPv6 localhost address.
|
||||||
window.location.hostname === '[::1]' ||
|
window.location.hostname === "[::1]" ||
|
||||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||||
window.location.hostname.match(
|
window.location.hostname.match(
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||||
@@ -21,7 +21,7 @@ const isLocalhost = Boolean(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function register(config) {
|
export function register(config) {
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
|
||||||
// The URL constructor is available in all browsers that support SW.
|
// The URL constructor is available in all browsers that support SW.
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
@@ -31,7 +31,7 @@ export function register(config) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener("load", () => {
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||||
|
|
||||||
if (isLocalhost) {
|
if (isLocalhost) {
|
||||||
@@ -42,8 +42,8 @@ export function register(config) {
|
|||||||
// service worker/PWA documentation.
|
// service worker/PWA documentation.
|
||||||
navigator.serviceWorker.ready.then(() => {
|
navigator.serviceWorker.ready.then(() => {
|
||||||
console.log(
|
console.log(
|
||||||
'This web app is being served cache-first by a service ' +
|
"This web app is being served cache-first by a service " +
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
"worker. To learn more, visit https://bit.ly/CRA-PWA"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -57,21 +57,21 @@ export function register(config) {
|
|||||||
function registerValidSW(swUrl, config) {
|
function registerValidSW(swUrl, config) {
|
||||||
navigator.serviceWorker
|
navigator.serviceWorker
|
||||||
.register(swUrl)
|
.register(swUrl)
|
||||||
.then(registration => {
|
.then((registration) => {
|
||||||
registration.onupdatefound = () => {
|
registration.onupdatefound = () => {
|
||||||
const installingWorker = registration.installing;
|
const installingWorker = registration.installing;
|
||||||
if (installingWorker == null) {
|
if (installingWorker == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
installingWorker.onstatechange = () => {
|
installingWorker.onstatechange = () => {
|
||||||
if (installingWorker.state === 'installed') {
|
if (installingWorker.state === "installed") {
|
||||||
if (navigator.serviceWorker.controller) {
|
if (navigator.serviceWorker.controller) {
|
||||||
// At this point, the updated precached content has been fetched,
|
// At this point, the updated precached content has been fetched,
|
||||||
// but the previous service worker will still serve the older
|
// but the previous service worker will still serve the older
|
||||||
// content until all client tabs are closed.
|
// content until all client tabs are closed.
|
||||||
console.log(
|
console.log(
|
||||||
'New content is available and will be used when all ' +
|
"New content is available and will be used when all " +
|
||||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute callback
|
// Execute callback
|
||||||
@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
|
|||||||
// At this point, everything has been precached.
|
// At this point, everything has been precached.
|
||||||
// It's the perfect time to display a
|
// It's the perfect time to display a
|
||||||
// "Content is cached for offline use." message.
|
// "Content is cached for offline use." message.
|
||||||
console.log('Content is cached for offline use.');
|
console.log("Content is cached for offline use.");
|
||||||
|
|
||||||
// Execute callback
|
// Execute callback
|
||||||
if (config && config.onSuccess) {
|
if (config && config.onSuccess) {
|
||||||
@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error('Error during service worker registration:', error);
|
console.error("Error during service worker registration:", error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl, config) {
|
function checkValidServiceWorker(swUrl, config) {
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
// Check if the service worker can be found. If it can't reload the page.
|
||||||
fetch(swUrl, {
|
fetch(swUrl, {
|
||||||
headers: { 'Service-Worker': 'script' },
|
headers: { "Service-Worker": "script" }
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
// Ensure service worker exists, and that we really are getting a JS file.
|
||||||
const contentType = response.headers.get('content-type');
|
const contentType = response.headers.get("content-type");
|
||||||
if (
|
if (
|
||||||
response.status === 404 ||
|
response.status === 404 ||
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
(contentType != null && contentType.indexOf("javascript") === -1)
|
||||||
) {
|
) {
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
// No service worker found. Probably a different app. Reload the page.
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
navigator.serviceWorker.ready.then((registration) => {
|
||||||
registration.unregister().then(() => {
|
registration.unregister().then(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
@@ -123,18 +123,18 @@ function checkValidServiceWorker(swUrl, config) {
|
|||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.log(
|
console.log(
|
||||||
'No internet connection found. App is running in offline mode.'
|
"No internet connection found. App is running in offline mode."
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unregister() {
|
export function unregister() {
|
||||||
if ('serviceWorker' in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.ready
|
navigator.serviceWorker.ready
|
||||||
.then(registration => {
|
.then((registration) => {
|
||||||
registration.unregister();
|
registration.unregister();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error(error.message);
|
console.error(error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom/extend-expect';
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import SASjs, { ServerType, SASjsConfig } from "sasjs";
|
import SASjs, { ServerType, SASjsConfig } from "@sasjs/adapter";
|
||||||
import { TestSuite } from "../types";
|
import { TestSuite } from "@sasjs/test-framework";
|
||||||
|
|
||||||
const defaultConfig: SASjsConfig = {
|
const defaultConfig: SASjsConfig = {
|
||||||
serverUrl: window.location.origin,
|
serverUrl: window.location.origin,
|
||||||
@@ -9,6 +9,7 @@ const defaultConfig: SASjsConfig = {
|
|||||||
serverType: ServerType.SASViya,
|
serverType: ServerType.SASViya,
|
||||||
debug: true,
|
debug: true,
|
||||||
contextName: "SAS Job Execution compute context",
|
contextName: "SAS Job Execution compute context",
|
||||||
|
useComputeApi: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const customConfig = {
|
const customConfig = {
|
||||||
@@ -17,7 +18,7 @@ const customConfig = {
|
|||||||
pathSASViya: "viya",
|
pathSASViya: "viya",
|
||||||
appLoc: "/Public/seedapp",
|
appLoc: "/Public/seedapp",
|
||||||
serverType: ServerType.SAS9,
|
serverType: ServerType.SAS9,
|
||||||
debug: false,
|
debug: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const basicTests = (
|
export const basicTests = (
|
||||||
@@ -34,7 +35,7 @@ export const basicTests = (
|
|||||||
return adapter.logIn(userName, password);
|
return adapter.logIn(userName, password);
|
||||||
},
|
},
|
||||||
assertion: (response: any) =>
|
assertion: (response: any) =>
|
||||||
response && response.isLoggedIn && response.userName === userName,
|
response && response.isLoggedIn && response.userName === userName
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Default config",
|
title: "Default config",
|
||||||
@@ -53,7 +54,7 @@ export const basicTests = (
|
|||||||
sasjsConfig.serverType === defaultConfig.serverType &&
|
sasjsConfig.serverType === defaultConfig.serverType &&
|
||||||
sasjsConfig.debug === defaultConfig.debug
|
sasjsConfig.debug === defaultConfig.debug
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Custom config",
|
title: "Custom config",
|
||||||
@@ -71,7 +72,7 @@ export const basicTests = (
|
|||||||
sasjsConfig.serverType === customConfig.serverType &&
|
sasjsConfig.serverType === customConfig.serverType &&
|
||||||
sasjsConfig.debug === customConfig.debug
|
sasjsConfig.debug === customConfig.debug
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Config overrides",
|
title: "Config overrides",
|
||||||
@@ -91,7 +92,7 @@ export const basicTests = (
|
|||||||
sasjsConfig.serverType === defaultConfig.serverType &&
|
sasjsConfig.serverType === defaultConfig.serverType &&
|
||||||
sasjsConfig.debug === false
|
sasjsConfig.debug === false
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import SASjs from "sasjs";
|
import SASjs from "@sasjs/adapter";
|
||||||
import { TestSuite } from "../types";
|
import { TestSuite } from "@sasjs/test-framework";
|
||||||
|
|
||||||
const stringData: any = { table1: [{ col1: "first col value" }] };
|
const stringData: any = { table1: [{ col1: "first col value" }] };
|
||||||
const numericData: any = { table1: [{ col1: 3.14159265 }] };
|
const numericData: any = { table1: [{ col1: 3.14159265 }] };
|
||||||
const multiColumnData: any = {
|
const multiColumnData: any = {
|
||||||
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }],
|
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }]
|
||||||
};
|
};
|
||||||
const multipleRowsWithNulls: any = {
|
const multipleRowsWithNulls: any = {
|
||||||
table1: [
|
table1: [
|
||||||
@@ -12,8 +12,8 @@ const multipleRowsWithNulls: any = {
|
|||||||
{ col1: 42, col2: null, col3: "x", col4: "" },
|
{ col1: 42, col2: null, col3: "x", col4: "" },
|
||||||
{ col1: 42, col2: null, col3: "x", col4: "" },
|
{ col1: 42, col2: null, col3: "x", col4: "" },
|
||||||
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
|
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
|
||||||
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
|
{ col1: 42, col2: 1.62, col3: "x", col4: "x" }
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
const multipleColumnsWithNulls: any = {
|
const multipleColumnsWithNulls: any = {
|
||||||
table1: [
|
table1: [
|
||||||
@@ -21,8 +21,8 @@ const multipleColumnsWithNulls: any = {
|
|||||||
{ col1: 42, col2: null, col3: "x", col4: null },
|
{ col1: 42, col2: null, col3: "x", col4: null },
|
||||||
{ col1: 42, col2: null, col3: "x", col4: null },
|
{ col1: 42, col2: null, col3: "x", col4: null },
|
||||||
{ col1: 42, col2: null, col3: "x", col4: "" },
|
{ col1: 42, col2: null, col3: "x", col4: "" },
|
||||||
{ col1: 42, col2: null, col3: "x", col4: "" },
|
{ col1: 42, col2: null, col3: "x", col4: "" }
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLongStringData = (length = 32764) => {
|
const getLongStringData = (length = 32764) => {
|
||||||
@@ -55,7 +55,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
return res.table1[0][0] === stringData.table1[0].col1;
|
return res.table1[0][0] === stringData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Long string value",
|
title: "Long string value",
|
||||||
@@ -67,20 +67,19 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
const longStringData = getLongStringData();
|
const longStringData = getLongStringData();
|
||||||
return res.table1[0][0] === longStringData.table1[0].col1;
|
return res.table1[0][0] === longStringData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Overly long string value",
|
title: "Overly long string value",
|
||||||
description:
|
description:
|
||||||
"Should error out with long string values over 32765 characters",
|
"Should error out with long string values over 32765 characters",
|
||||||
test: () => {
|
test: () => {
|
||||||
return adapter
|
const data = getLongStringData(32767);
|
||||||
.request("common/sendArr", getLongStringData(32767))
|
return adapter.request("common/sendArr", data).catch((e) => e);
|
||||||
.catch((e) => e);
|
|
||||||
},
|
},
|
||||||
assertion: (error: any) => {
|
assertion: (error: any) => {
|
||||||
return !!error && !!error.MESSAGE;
|
return !!error && !!error.MESSAGE;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Single numeric value",
|
title: "Single numeric value",
|
||||||
@@ -90,7 +89,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
return res.table1[0][0] === numericData.table1[0].col1;
|
return res.table1[0][0] === numericData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple columns",
|
title: "Multiple columns",
|
||||||
@@ -105,7 +104,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
res.table1[0][2] === multiColumnData.table1[0].col3 &&
|
res.table1[0][2] === multiColumnData.table1[0].col3 &&
|
||||||
res.table1[0][3] === multiColumnData.table1[0].col4
|
res.table1[0][3] === multiColumnData.table1[0].col4
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple rows with nulls",
|
title: "Multiple rows with nulls",
|
||||||
@@ -130,7 +129,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
res.table1[index][3] === multipleRowsWithNulls.table1[index].col4;
|
res.table1[index][3] === multipleRowsWithNulls.table1[index].col4;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple columns with nulls",
|
title: "Multiple columns with nulls",
|
||||||
@@ -159,9 +158,9 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
(multipleColumnsWithNulls.table1[index].col4 || "");
|
(multipleColumnsWithNulls.table1[index].col4 || "");
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
||||||
@@ -172,11 +171,11 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
description: "Should throw an error",
|
description: "Should throw an error",
|
||||||
test: async () => {
|
test: async () => {
|
||||||
const invalidData: any = {
|
const invalidData: any = {
|
||||||
"1 invalid table": [{ col1: 42 }],
|
"1 invalid table": [{ col1: 42 }]
|
||||||
};
|
};
|
||||||
return adapter.request("common/sendObj", invalidData).catch((e) => e);
|
return adapter.request("common/sendObj", invalidData).catch((e) => e);
|
||||||
},
|
},
|
||||||
assertion: (error: any) => !!error && !!error.MESSAGE,
|
assertion: (error: any) => !!error && !!error.MESSAGE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Single string value",
|
title: "Single string value",
|
||||||
@@ -186,7 +185,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
return res.table1[0].COL1 === stringData.table1[0].col1;
|
return res.table1[0].COL1 === stringData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Long string value",
|
title: "Long string value",
|
||||||
@@ -198,7 +197,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
const longStringData = getLongStringData();
|
const longStringData = getLongStringData();
|
||||||
return res.table1[0].COL1 === longStringData.table1[0].col1;
|
return res.table1[0].COL1 === longStringData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Overly long string value",
|
title: "Overly long string value",
|
||||||
@@ -211,7 +210,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
},
|
},
|
||||||
assertion: (error: any) => {
|
assertion: (error: any) => {
|
||||||
return !!error && !!error.MESSAGE;
|
return !!error && !!error.MESSAGE;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Single numeric value",
|
title: "Single numeric value",
|
||||||
@@ -221,7 +220,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
return res.table1[0].COL1 === numericData.table1[0].col1;
|
return res.table1[0].COL1 === numericData.table1[0].col1;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -233,7 +232,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
const data = getLargeObjectData();
|
const data = getLargeObjectData();
|
||||||
return res.table1[9000].BIG === data.table1[9000].big;
|
return res.table1[9000].BIG === data.table1[9000].big;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple columns",
|
title: "Multiple columns",
|
||||||
@@ -248,7 +247,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
res.table1[0].COL3 === multiColumnData.table1[0].col3 &&
|
res.table1[0].COL3 === multiColumnData.table1[0].col3 &&
|
||||||
res.table1[0].COL4 === multiColumnData.table1[0].col4
|
res.table1[0].COL4 === multiColumnData.table1[0].col4
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple rows with nulls",
|
title: "Multiple rows with nulls",
|
||||||
@@ -273,7 +272,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
res.table1[index].COL4 === multipleRowsWithNulls.table1[index].col4;
|
res.table1[index].COL4 === multipleRowsWithNulls.table1[index].col4;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Multiple columns with nulls",
|
title: "Multiple columns with nulls",
|
||||||
@@ -302,7 +301,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
(multipleColumnsWithNulls.table1[index].col4 || "");
|
(multipleColumnsWithNulls.table1[index].col4 || "");
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import SASjs from "sasjs";
|
import SASjs from "@sasjs/adapter";
|
||||||
import { TestSuite } from "../types";
|
import { TestSuite } from "@sasjs/test-framework";
|
||||||
|
|
||||||
const data: any = { table1: [{ col1: "first col value" }] };
|
const data: any = { table1: [{ col1: "first col value" }] };
|
||||||
|
|
||||||
@@ -12,14 +12,39 @@ export const sasjsRequestTests = (adapter: SASjs): TestSuite => ({
|
|||||||
test: async () => {
|
test: async () => {
|
||||||
return adapter.request("common/sendArr", data);
|
return adapter.request("common/sendArr", data);
|
||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: () => {
|
||||||
const requests = adapter.getSasRequests();
|
const requests = adapter.getSasRequests();
|
||||||
if (adapter.getSasjsConfig().debug) {
|
if (adapter.getSasjsConfig().debug) {
|
||||||
return requests[0].SASWORK !== null;
|
return requests[0].SASWORK !== null;
|
||||||
} else {
|
} else {
|
||||||
return requests[0].SASWORK === null;
|
return requests[0].SASWORK === null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
],
|
title: "Make error and capture log",
|
||||||
|
description: "Should make an error and capture log",
|
||||||
|
test: async () => {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
adapter
|
||||||
|
.request("common/makeErr", data)
|
||||||
|
.then((res) => {
|
||||||
|
//no action here, this request must throw error
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
let sasRequests = adapter.getSasRequests();
|
||||||
|
let makeErrRequest =
|
||||||
|
sasRequests.find((req) =>
|
||||||
|
req.serviceLink.includes("makeErr")
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
resolve(!!makeErrRequest);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
assertion: (response) => {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import SASjs from "sasjs";
|
import SASjs from "@sasjs/adapter";
|
||||||
import { TestSuite } from "../types";
|
import { TestSuite } from "@sasjs/test-framework";
|
||||||
|
|
||||||
const specialCharData: any = {
|
const specialCharData: any = {
|
||||||
table1: [
|
table1: [
|
||||||
@@ -13,9 +13,9 @@ const specialCharData: any = {
|
|||||||
doubleQuote: '"',
|
doubleQuote: '"',
|
||||||
crlf: "\r\n",
|
crlf: "\r\n",
|
||||||
euro: "€euro",
|
euro: "€euro",
|
||||||
banghash: "!#banghash",
|
banghash: "!#banghash"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const moreSpecialCharData: any = {
|
const moreSpecialCharData: any = {
|
||||||
@@ -31,9 +31,9 @@ const moreSpecialCharData: any = {
|
|||||||
sigma: "Σsigma",
|
sigma: "Σsigma",
|
||||||
at: "@at",
|
at: "@at",
|
||||||
serbian: "Српски",
|
serbian: "Српски",
|
||||||
dollar: "$",
|
dollar: "$"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWideData = () => {
|
const getWideData = () => {
|
||||||
@@ -43,7 +43,7 @@ const getWideData = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data: any = {
|
const data: any = {
|
||||||
table1: [cols],
|
table1: [cols]
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -67,7 +67,7 @@ const getLargeDataset = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data: any = {
|
const data: any = {
|
||||||
table1: rows,
|
table1: rows
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -75,7 +75,7 @@ const getLargeDataset = () => {
|
|||||||
|
|
||||||
const errorAndCsrfData: any = {
|
const errorAndCsrfData: any = {
|
||||||
error: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
|
error: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
|
||||||
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
|
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
||||||
@@ -100,88 +100,88 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
|||||||
res.table1[0][8] === specialCharData.table1[0].euro &&
|
res.table1[0][8] === specialCharData.table1[0].euro &&
|
||||||
res.table1[0][9] === specialCharData.table1[0].banghash
|
res.table1[0][9] === specialCharData.table1[0].banghash
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
// {
|
|
||||||
// title: "Other special characters",
|
|
||||||
// description: "Should handle other special characters",
|
|
||||||
// test: () => {
|
|
||||||
// return adapter.request("common/sendArr", moreSpecialCharData);
|
|
||||||
// },
|
|
||||||
// assertion: (res: any) => {
|
|
||||||
// return (
|
|
||||||
// res.table1[0][0] === moreSpecialCharData.table1[0].speech0 &&
|
|
||||||
// res.table1[0][1] === moreSpecialCharData.table1[0].pct &&
|
|
||||||
// res.table1[0][2] === moreSpecialCharData.table1[0].speech &&
|
|
||||||
// res.table1[0][3] === moreSpecialCharData.table1[0].slash &&
|
|
||||||
// res.table1[0][4] === moreSpecialCharData.table1[0].slashWithSpecial &&
|
|
||||||
// res.table1[0][5] === moreSpecialCharData.table1[0].macvar &&
|
|
||||||
// res.table1[0][6] === moreSpecialCharData.table1[0].chinese &&
|
|
||||||
// res.table1[0][7] === moreSpecialCharData.table1[0].sigma &&
|
|
||||||
// res.table1[0][8] === moreSpecialCharData.table1[0].at &&
|
|
||||||
// res.table1[0][9] === moreSpecialCharData.table1[0].serbian &&
|
|
||||||
// res.table1[0][10] === moreSpecialCharData.table1[0].dollar
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Wide table with sendArr",
|
|
||||||
// description: "Should handle data with 10000 columns",
|
|
||||||
// test: () => {
|
|
||||||
// return adapter.request("common/sendArr", getWideData());
|
|
||||||
// },
|
|
||||||
// assertion: (res: any) => {
|
|
||||||
// const data = getWideData();
|
|
||||||
// let result = true;
|
|
||||||
// for (let i = 0; i <= 10; i++) {
|
|
||||||
// result =
|
|
||||||
// result && res.table1[0][i] === data.table1[0]["col" + (i + 1)];
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Wide table with sendObj",
|
|
||||||
// description: "Should handle data with 10000 columns",
|
|
||||||
// test: () => {
|
|
||||||
// return adapter.request("common/sendObj", getWideData());
|
|
||||||
// },
|
|
||||||
// assertion: (res: any) => {
|
|
||||||
// const data = getWideData();
|
|
||||||
// let result = true;
|
|
||||||
// for (let i = 0; i <= 10; i++) {
|
|
||||||
// result =
|
|
||||||
// result &&
|
|
||||||
// res.table1[0]["COL" + (i + 1)] === data.table1[0]["col" + (i + 1)];
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Multiple tables",
|
|
||||||
// description: "Should handle data with 100 tables",
|
|
||||||
// test: () => {
|
|
||||||
// return adapter.request("common/sendArr", getTables());
|
|
||||||
// },
|
|
||||||
// assertion: (res: any) => {
|
|
||||||
// const data = getTables();
|
|
||||||
// return (
|
|
||||||
// res.table1[0][0] === data.table1[0].col1 &&
|
|
||||||
// res.table1[0][1] === data.table1[0].col2 &&
|
|
||||||
// res.table1[0][2] === data.table1[0].col3 &&
|
|
||||||
// res.table1[0][3] === data.table1[0].col4 &&
|
|
||||||
// res.table50[0][0] === data.table50[0].col1 &&
|
|
||||||
// res.table50[0][1] === data.table50[0].col2 &&
|
|
||||||
// res.table50[0][2] === data.table50[0].col3 &&
|
|
||||||
// res.table50[0][3] === data.table50[0].col4
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: "Large dataset",
|
title: "Other special characters",
|
||||||
|
description: "Should handle other special characters",
|
||||||
|
test: () => {
|
||||||
|
return adapter.request("common/sendArr", moreSpecialCharData);
|
||||||
|
},
|
||||||
|
assertion: (res: any) => {
|
||||||
|
return (
|
||||||
|
res.table1[0][0] === moreSpecialCharData.table1[0].speech0 &&
|
||||||
|
res.table1[0][1] === moreSpecialCharData.table1[0].pct &&
|
||||||
|
res.table1[0][2] === moreSpecialCharData.table1[0].speech &&
|
||||||
|
res.table1[0][3] === moreSpecialCharData.table1[0].slash &&
|
||||||
|
res.table1[0][4] === moreSpecialCharData.table1[0].slashWithSpecial &&
|
||||||
|
res.table1[0][5] === moreSpecialCharData.table1[0].macvar &&
|
||||||
|
res.table1[0][6] === moreSpecialCharData.table1[0].chinese &&
|
||||||
|
res.table1[0][7] === moreSpecialCharData.table1[0].sigma &&
|
||||||
|
res.table1[0][8] === moreSpecialCharData.table1[0].at &&
|
||||||
|
res.table1[0][9] === moreSpecialCharData.table1[0].serbian &&
|
||||||
|
res.table1[0][10] === moreSpecialCharData.table1[0].dollar
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Wide table with sendArr",
|
||||||
|
description: "Should handle data with 10000 columns",
|
||||||
|
test: () => {
|
||||||
|
return adapter.request("common/sendArr", getWideData());
|
||||||
|
},
|
||||||
|
assertion: (res: any) => {
|
||||||
|
const data = getWideData();
|
||||||
|
let result = true;
|
||||||
|
for (let i = 0; i <= 10; i++) {
|
||||||
|
result =
|
||||||
|
result && res.table1[0][i] === data.table1[0]["col" + (i + 1)];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Wide table with sendObj",
|
||||||
|
description: "Should handle data with 10000 columns",
|
||||||
|
test: () => {
|
||||||
|
return adapter.request("common/sendObj", getWideData());
|
||||||
|
},
|
||||||
|
assertion: (res: any) => {
|
||||||
|
const data = getWideData();
|
||||||
|
let result = true;
|
||||||
|
for (let i = 0; i <= 10; i++) {
|
||||||
|
result =
|
||||||
|
result &&
|
||||||
|
res.table1[0]["COL" + (i + 1)] === data.table1[0]["col" + (i + 1)];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Multiple tables",
|
||||||
|
description: "Should handle data with 100 tables",
|
||||||
|
test: () => {
|
||||||
|
return adapter.request("common/sendArr", getTables());
|
||||||
|
},
|
||||||
|
assertion: (res: any) => {
|
||||||
|
const data = getTables();
|
||||||
|
return (
|
||||||
|
res.table1[0][0] === data.table1[0].col1 &&
|
||||||
|
res.table1[0][1] === data.table1[0].col2 &&
|
||||||
|
res.table1[0][2] === data.table1[0].col3 &&
|
||||||
|
res.table1[0][3] === data.table1[0].col4 &&
|
||||||
|
res.table50[0][0] === data.table50[0].col1 &&
|
||||||
|
res.table50[0][1] === data.table50[0].col2 &&
|
||||||
|
res.table50[0][2] === data.table50[0].col3 &&
|
||||||
|
res.table50[0][3] === data.table50[0].col4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Large dataset with sendObj",
|
||||||
description: "Should handle 5mb of data",
|
description: "Should handle 5mb of data",
|
||||||
test: () => {
|
test: () => {
|
||||||
return adapter.request("common/sendArr", getLargeDataset());
|
return adapter.request("common/sendObj", getLargeDataset());
|
||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
const data = getLargeDataset();
|
const data = getLargeDataset();
|
||||||
@@ -190,46 +190,61 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
|||||||
result = result && res.table1[i][0] === data.table1[i][0];
|
result = result && res.table1[i][0] === data.table1[i][0];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Large dataset with sendArr",
|
||||||
|
description: "Should handle 5mb of data",
|
||||||
|
test: () => {
|
||||||
|
return adapter.request("common/sendArr", getLargeDataset());
|
||||||
},
|
},
|
||||||
|
assertion: (res: any) => {
|
||||||
// {
|
const data = getLargeDataset();
|
||||||
// title: "Error and _csrf tables with sendArr",
|
let result = true;
|
||||||
// description: "Should handle error and _csrf tables",
|
for (let i = 0; i <= 10; i++) {
|
||||||
// test: () => {
|
result =
|
||||||
// return adapter.request("common/sendArr", errorAndCsrfData);
|
result && res.table1[i][0] === Object.values(data.table1[i])[0];
|
||||||
// },
|
}
|
||||||
// assertion: (res: any) => {
|
return result;
|
||||||
// return (
|
}
|
||||||
// res.error[0][0] === errorAndCsrfData.error[0].col1 &&
|
},
|
||||||
// res.error[0][1] === errorAndCsrfData.error[0].col2 &&
|
{
|
||||||
// res.error[0][2] === errorAndCsrfData.error[0].col3 &&
|
title: "Error and _csrf tables with sendArr",
|
||||||
// res.error[0][3] === errorAndCsrfData.error[0].col4 &&
|
description: "Should handle error and _csrf tables",
|
||||||
// res._csrf[0][0] === errorAndCsrfData._csrf[0].col1 &&
|
test: () => {
|
||||||
// res._csrf[0][1] === errorAndCsrfData._csrf[0].col2 &&
|
return adapter.request("common/sendArr", errorAndCsrfData);
|
||||||
// res._csrf[0][2] === errorAndCsrfData._csrf[0].col3 &&
|
},
|
||||||
// res._csrf[0][3] === errorAndCsrfData._csrf[0].col4
|
assertion: (res: any) => {
|
||||||
// );
|
return (
|
||||||
// },
|
res.error[0][0] === errorAndCsrfData.error[0].col1 &&
|
||||||
// },
|
res.error[0][1] === errorAndCsrfData.error[0].col2 &&
|
||||||
// {
|
res.error[0][2] === errorAndCsrfData.error[0].col3 &&
|
||||||
// title: "Error and _csrf tables with sendObj",
|
res.error[0][3] === errorAndCsrfData.error[0].col4 &&
|
||||||
// description: "Should handle error and _csrf tables",
|
res._csrf[0][0] === errorAndCsrfData._csrf[0].col1 &&
|
||||||
// test: () => {
|
res._csrf[0][1] === errorAndCsrfData._csrf[0].col2 &&
|
||||||
// return adapter.request("common/sendObj", errorAndCsrfData);
|
res._csrf[0][2] === errorAndCsrfData._csrf[0].col3 &&
|
||||||
// },
|
res._csrf[0][3] === errorAndCsrfData._csrf[0].col4
|
||||||
// assertion: (res: any) => {
|
);
|
||||||
// return (
|
}
|
||||||
// res.error[0].COL1 === errorAndCsrfData.error[0].col1 &&
|
},
|
||||||
// res.error[0].COL2 === errorAndCsrfData.error[0].col2 &&
|
{
|
||||||
// res.error[0].COL3 === errorAndCsrfData.error[0].col3 &&
|
title: "Error and _csrf tables with sendObj",
|
||||||
// res.error[0].COL4 === errorAndCsrfData.error[0].col4 &&
|
description: "Should handle error and _csrf tables",
|
||||||
// res._csrf[0].COL1 === errorAndCsrfData._csrf[0].col1 &&
|
test: () => {
|
||||||
// res._csrf[0].COL2 === errorAndCsrfData._csrf[0].col2 &&
|
return adapter.request("common/sendObj", errorAndCsrfData);
|
||||||
// res._csrf[0].COL3 === errorAndCsrfData._csrf[0].col3 &&
|
},
|
||||||
// res._csrf[0].COL4 === errorAndCsrfData._csrf[0].col4
|
assertion: (res: any) => {
|
||||||
// );
|
return (
|
||||||
// },
|
res.error[0].COL1 === errorAndCsrfData.error[0].col1 &&
|
||||||
// },
|
res.error[0].COL2 === errorAndCsrfData.error[0].col2 &&
|
||||||
],
|
res.error[0].COL3 === errorAndCsrfData.error[0].col3 &&
|
||||||
|
res.error[0].COL4 === errorAndCsrfData.error[0].col4 &&
|
||||||
|
res._csrf[0].COL1 === errorAndCsrfData._csrf[0].col1 &&
|
||||||
|
res._csrf[0].COL2 === errorAndCsrfData._csrf[0].col2 &&
|
||||||
|
res._csrf[0].COL3 === errorAndCsrfData._csrf[0].col3 &&
|
||||||
|
res._csrf[0].COL4 === errorAndCsrfData._csrf[0].col4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
export interface Test {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
beforeTest?: (...args: any) => Promise<any>;
|
|
||||||
afterTest?: (...args: any) => Promise<any>;
|
|
||||||
test: (context?: any) => Promise<any>;
|
|
||||||
assertion: (...args: any) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TestSuite {
|
|
||||||
name: string;
|
|
||||||
tests: Test[];
|
|
||||||
beforeAll?: (...args: any) => Promise<any>;
|
|
||||||
afterAll?: (...args: any) => Promise<any>;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
export const uploadFile = (file: File, fileName: string, url: string) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const data = new FormData();
|
|
||||||
data.append("file", file);
|
|
||||||
data.append("filename", fileName);
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.withCredentials = true;
|
|
||||||
xhr.addEventListener("readystatechange", function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
let response: any;
|
|
||||||
try {
|
|
||||||
response = JSON.parse(this.responseText);
|
|
||||||
} catch (e) {
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
xhr.open("POST", url);
|
|
||||||
xhr.setRequestHeader("cache-control", "no-cache");
|
|
||||||
xhr.send(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
100
src/FileUploader.ts
Normal file
100
src/FileUploader.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { isLogInRequired, needsRetry } from './utils'
|
||||||
|
import { CsrfToken } from './types/CsrfToken'
|
||||||
|
import { UploadFile } from './types/UploadFile'
|
||||||
|
|
||||||
|
const requestRetryLimit = 5
|
||||||
|
|
||||||
|
export class FileUploader {
|
||||||
|
constructor(
|
||||||
|
private appLoc: string,
|
||||||
|
private serverUrl: string,
|
||||||
|
private jobsPath: string,
|
||||||
|
private setCsrfTokenWeb: any,
|
||||||
|
private csrfToken: CsrfToken | null = null
|
||||||
|
) {}
|
||||||
|
private retryCount = 0
|
||||||
|
|
||||||
|
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
||||||
|
if (files?.length < 1) throw new Error('Atleast one file must be provided')
|
||||||
|
|
||||||
|
let paramsString = ''
|
||||||
|
|
||||||
|
for (let param in params) {
|
||||||
|
if (params.hasOwnProperty(param)) {
|
||||||
|
paramsString += `&${param}=${params[param]}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const program = this.appLoc
|
||||||
|
? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
|
||||||
|
: sasJob
|
||||||
|
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
||||||
|
'_program=' + program
|
||||||
|
}${paramsString}`
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'cache-control': 'no-cache'
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
for (let file of files) {
|
||||||
|
formData.append('file', file.file, file.fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.csrfToken) formData.append('_csrf', this.csrfToken.value)
|
||||||
|
|
||||||
|
fetch(uploadUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
referrerPolicy: 'same-origin',
|
||||||
|
headers
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 403) {
|
||||||
|
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||||
|
|
||||||
|
if (tokenHeader) {
|
||||||
|
const token = response.headers.get(tokenHeader)
|
||||||
|
this.csrfToken = {
|
||||||
|
headerName: tokenHeader,
|
||||||
|
value: token || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCsrfTokenWeb(this.csrfToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text()
|
||||||
|
})
|
||||||
|
.then((responseText) => {
|
||||||
|
if (isLogInRequired(responseText))
|
||||||
|
reject('You must be logged in to upload a fle')
|
||||||
|
|
||||||
|
if (needsRetry(responseText)) {
|
||||||
|
if (this.retryCount < requestRetryLimit) {
|
||||||
|
this.retryCount++
|
||||||
|
this.uploadFile(sasJob, files, params).then(
|
||||||
|
(res: any) => resolve(res),
|
||||||
|
(err: any) => reject(err)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.retryCount = 0
|
||||||
|
reject(responseText)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.retryCount = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(responseText))
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,8 @@ export class SAS9ApiClient {
|
|||||||
*/
|
*/
|
||||||
public getConfig() {
|
public getConfig() {
|
||||||
return {
|
return {
|
||||||
serverUrl: this.serverUrl,
|
serverUrl: this.serverUrl
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +19,7 @@ export class SAS9ApiClient {
|
|||||||
* @param serverUrl - the URL of the server.
|
* @param serverUrl - the URL of the server.
|
||||||
*/
|
*/
|
||||||
public setConfig(serverUrl: string) {
|
public setConfig(serverUrl: string) {
|
||||||
if (serverUrl) this.serverUrl = serverUrl;
|
if (serverUrl) this.serverUrl = serverUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,19 +33,19 @@ export class SAS9ApiClient {
|
|||||||
serverName: string,
|
serverName: string,
|
||||||
repositoryName: string
|
repositoryName: string
|
||||||
) {
|
) {
|
||||||
const requestPayload = linesOfCode.join("\n");
|
const requestPayload = linesOfCode.join('\n')
|
||||||
const executeScriptRequest = {
|
const executeScriptRequest = {
|
||||||
method: "PUT",
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: 'application/json'
|
||||||
},
|
},
|
||||||
body: `command=${requestPayload}`,
|
body: `command=${requestPayload}`
|
||||||
};
|
}
|
||||||
const executeScriptResponse = await fetch(
|
const executeScriptResponse = await fetch(
|
||||||
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
||||||
executeScriptRequest
|
executeScriptRequest
|
||||||
).then((res) => res.text());
|
).then((res) => res.text())
|
||||||
|
|
||||||
return executeScriptResponse;
|
return executeScriptResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,36 @@
|
|||||||
import SASjs from "./index";
|
import SASjs from './index'
|
||||||
|
|
||||||
const adapter = new SASjs();
|
const adapter = new SASjs()
|
||||||
|
|
||||||
it("should parse SAS9 source code", async done => {
|
it('should parse SAS9 source code', async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
|
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse)
|
||||||
expect(parsedSourceCode).toBeTruthy();
|
expect(parsedSourceCode).toBeTruthy()
|
||||||
const sourceCodeLines = parsedSourceCode.split("\r\n");
|
const sourceCodeLines = parsedSourceCode.split('\r\n')
|
||||||
expect(sourceCodeLines.length).toEqual(5);
|
expect(sourceCodeLines.length).toEqual(5)
|
||||||
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy();
|
expect(sourceCodeLines[0].startsWith('6')).toBeTruthy()
|
||||||
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy();
|
expect(sourceCodeLines[1].startsWith('7')).toBeTruthy()
|
||||||
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy();
|
expect(sourceCodeLines[2].startsWith('8')).toBeTruthy()
|
||||||
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy();
|
expect(sourceCodeLines[3].startsWith('9')).toBeTruthy()
|
||||||
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy();
|
expect(sourceCodeLines[4].startsWith('10')).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should parse generated code", async done => {
|
it('should parse generated code', async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
||||||
sampleResponse
|
sampleResponse
|
||||||
);
|
)
|
||||||
expect(parsedGeneratedCode).toBeTruthy();
|
expect(parsedGeneratedCode).toBeTruthy()
|
||||||
const generatedCodeLines = parsedGeneratedCode.split("\r\n");
|
const generatedCodeLines = parsedGeneratedCode.split('\r\n')
|
||||||
expect(generatedCodeLines.length).toEqual(5);
|
expect(generatedCodeLines.length).toEqual(5)
|
||||||
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy();
|
expect(generatedCodeLines[0].startsWith('MPRINT(MM_WEBIN)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy();
|
expect(generatedCodeLines[1].startsWith('MPRINT(MM_WEBLEFT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[2].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy();
|
expect(generatedCodeLines[3].startsWith('MPRINT(MM_WEBRIGHT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[4].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
|
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
|
||||||
@@ -44,5 +44,5 @@ MPRINT(MM_WEBLEFT): filename _temp temp lrecl=999999;
|
|||||||
MPRINT(MM_WEBOUT): data _null_;
|
MPRINT(MM_WEBOUT): data _null_;
|
||||||
MPRINT(MM_WEBRIGHT): file _temp;
|
MPRINT(MM_WEBRIGHT): file _temp;
|
||||||
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
||||||
`;
|
`
|
||||||
/* tslint:enable */
|
/* tslint:enable */
|
||||||
|
|||||||
1244
src/SASjs.ts
1244
src/SASjs.ts
File diff suppressed because it is too large
Load Diff
170
src/SessionManager.ts
Normal file
170
src/SessionManager.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import { Session, Context, CsrfToken } from './types'
|
||||||
|
import { asyncForEach, makeRequest } from './utils'
|
||||||
|
|
||||||
|
const MAX_SESSION_COUNT = 1
|
||||||
|
|
||||||
|
export class SessionManager {
|
||||||
|
constructor(
|
||||||
|
private serverUrl: string,
|
||||||
|
private contextName: string,
|
||||||
|
private setCsrfToken: (csrfToken: CsrfToken) => void
|
||||||
|
) {}
|
||||||
|
private sessions: Session[] = []
|
||||||
|
private currentContext: Context | null = null
|
||||||
|
private csrfToken: CsrfToken | null = null
|
||||||
|
|
||||||
|
async getSession(accessToken?: string) {
|
||||||
|
await this.createSessions(accessToken)
|
||||||
|
this.createAndWaitForSession(accessToken)
|
||||||
|
const session = this.sessions.pop()
|
||||||
|
const secondsSinceSessionCreation =
|
||||||
|
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
||||||
|
1000
|
||||||
|
if (
|
||||||
|
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
||||||
|
) {
|
||||||
|
await this.createSessions(accessToken)
|
||||||
|
const freshSession = this.sessions.pop()
|
||||||
|
return freshSession
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearSession(id: string, accessToken?: string) {
|
||||||
|
const deleteSessionRequest = {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: this.getHeaders(accessToken)
|
||||||
|
}
|
||||||
|
return await this.request<Session>(
|
||||||
|
`${this.serverUrl}/compute/sessions/${id}`,
|
||||||
|
deleteSessionRequest
|
||||||
|
).then(() => {
|
||||||
|
this.sessions = this.sessions.filter((s) => s.id !== id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createSessions(accessToken?: string) {
|
||||||
|
if (!this.sessions.length) {
|
||||||
|
if (!this.currentContext) {
|
||||||
|
await this.setCurrentContext(accessToken)
|
||||||
|
}
|
||||||
|
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
|
||||||
|
const createdSession = await this.createAndWaitForSession(accessToken)
|
||||||
|
this.sessions.push(createdSession)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createAndWaitForSession(accessToken?: string) {
|
||||||
|
const createSessionRequest = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.getHeaders(accessToken)
|
||||||
|
}
|
||||||
|
const { result: createdSession, etag } = await this.request<Session>(
|
||||||
|
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
|
||||||
|
createSessionRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.waitForSession(createdSession, etag)
|
||||||
|
this.sessions.push(createdSession)
|
||||||
|
return createdSession
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setCurrentContext(accessToken?: string) {
|
||||||
|
if (!this.currentContext) {
|
||||||
|
const { result: contexts } = await this.request<{
|
||||||
|
items: Context[]
|
||||||
|
}>(`${this.serverUrl}/compute/contexts`, {
|
||||||
|
headers: this.getHeaders(accessToken)
|
||||||
|
})
|
||||||
|
|
||||||
|
const contextsList =
|
||||||
|
contexts && contexts.items && contexts.items.length
|
||||||
|
? contexts.items
|
||||||
|
: []
|
||||||
|
|
||||||
|
const currentContext = contextsList.find(
|
||||||
|
(c: any) => c.name === this.contextName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!currentContext) {
|
||||||
|
throw new Error(
|
||||||
|
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentContext = currentContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHeaders(accessToken?: string) {
|
||||||
|
const headers: any = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
if (accessToken) {
|
||||||
|
headers.Authorization = `Bearer ${accessToken}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitForSession(
|
||||||
|
session: Session,
|
||||||
|
etag: string | null,
|
||||||
|
accessToken?: string,
|
||||||
|
silent = false
|
||||||
|
) {
|
||||||
|
let sessionState = session.state
|
||||||
|
const headers: any = {
|
||||||
|
...this.getHeaders(accessToken),
|
||||||
|
'If-None-Match': etag
|
||||||
|
}
|
||||||
|
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
||||||
|
return new Promise(async (resolve, _) => {
|
||||||
|
if (sessionState === 'pending') {
|
||||||
|
if (stateLink) {
|
||||||
|
if (!silent) {
|
||||||
|
console.log('Polling session status... \n')
|
||||||
|
}
|
||||||
|
const { result: state } = await this.request<string>(
|
||||||
|
`${this.serverUrl}${stateLink.href}?wait=30`,
|
||||||
|
{
|
||||||
|
headers
|
||||||
|
},
|
||||||
|
'text'
|
||||||
|
)
|
||||||
|
|
||||||
|
sessionState = state.trim()
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`Current state: ${sessionState}\n`)
|
||||||
|
}
|
||||||
|
resolve(sessionState)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve(sessionState)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async request<T>(
|
||||||
|
url: string,
|
||||||
|
options: RequestInit,
|
||||||
|
contentType: 'text' | 'json' = 'json'
|
||||||
|
) {
|
||||||
|
if (this.csrfToken) {
|
||||||
|
options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
[this.csrfToken.headerName]: this.csrfToken.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await makeRequest<T>(
|
||||||
|
url,
|
||||||
|
options,
|
||||||
|
(token) => {
|
||||||
|
this.csrfToken = token
|
||||||
|
this.setCsrfToken(token)
|
||||||
|
},
|
||||||
|
contentType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/index.ts
10
src/index.ts
@@ -1,5 +1,5 @@
|
|||||||
import SASjs from "./SASjs";
|
import SASjs from './SASjs'
|
||||||
export * from "./types";
|
export * from './types'
|
||||||
export * from "./SASViyaApiClient";
|
export * from './SASViyaApiClient'
|
||||||
export * from "./SAS9ApiClient";
|
export * from './SAS9ApiClient'
|
||||||
export default SASjs;
|
export default SASjs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface Context {
|
export interface Context {
|
||||||
name: string;
|
name: string
|
||||||
id: string;
|
id: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
version: number;
|
version: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface CsrfToken {
|
export interface CsrfToken {
|
||||||
headerName: string;
|
headerName: string
|
||||||
value: string;
|
value: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from './Link'
|
||||||
|
|
||||||
export interface Folder {
|
export interface Folder {
|
||||||
id: string;
|
id: string
|
||||||
uri: string;
|
uri: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from './Link'
|
||||||
import { JobResult } from "./JobResult";
|
import { JobResult } from './JobResult'
|
||||||
|
|
||||||
export interface Job {
|
export interface Job {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
uri: string;
|
uri: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
links: Link[];
|
code?: string
|
||||||
results: JobResult;
|
links: Link[]
|
||||||
|
results: JobResult
|
||||||
|
error?: any
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/types/JobDefinition.ts
Normal file
3
src/types/JobDefinition.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface JobDefinition {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface JobResult {
|
export interface JobResult {
|
||||||
"_webout.json": string;
|
'_webout.json': string
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user