diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 7783d5c..687fc5a 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -6,7 +6,7 @@ name: SASjs Build and Publish on: push: branches: - - main + - master jobs: build: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 398693c..135d8de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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; filename ft15f001 temp; parmcards4; @@ -40,18 +40,13 @@ parmcards4; # 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; - filename ft15f001 temp; parmcards4; + %webout(FETCH) %webout(OPEN) - %global sasjs_tables; - %let sasjs_tables=&sasjs_tables; - %put &=sasjs_tables; - %let sasjs_tables=&sasjs_tables; %macro x(); - %global sasjs_tables; %do i=1 %to %sysfunc(countw(&sasjs_tables)); %let table=%scan(&sasjs_tables,&i); %webout(OBJ,&table) @@ -60,13 +55,11 @@ parmcards4; %x() %webout(CLOSE) ;;;; -%mv_createwebservice(path=/Public/app/common,name=sendObj) +%mp_createwebservice(path=/Public/app/common,name=sendObj) filename ft15f001 temp; parmcards4; + %webout(FETCH) %webout(OPEN) - %global sasjs_tables; - %let sasjs_tables=&sasjs_tables; - %put &=sasjs_tables; %macro x(); %do i=1 %to %sysfunc(countw(&sasjs_tables)); %let table=%scan(&sasjs_tables,&i); @@ -76,7 +69,7 @@ parmcards4; %x() %webout(CLOSE) ;;;; -%mv_createwebservice(path=/Public/app/common,name=sendArr) +%mp_createwebservice(path=/Public/app/common,name=sendArr) ``` The above services will return anything you send. To run the tests simply launch `npm run cypress`. diff --git a/README.md b/README.md index 839664c..47ef0f1 100644 --- a/README.md +++ b/README.md @@ -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. -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) ## 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: @@ -43,6 +43,6 @@ You now have a simple web app with a backend service! # 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. diff --git a/docs/index.html b/docs/index.html index bf0b429..a3514df 100644 --- a/docs/index.html +++ b/docs/index.html @@ -199,7 +199,7 @@ parmcards4;

For more information specific to this adapter you can check out this - user guide or + user guide or the technical documentation.

@@ -225,7 +225,7 @@ parmcards4;

Generated using - TypeDoc + TypeDoc

diff --git a/example.html b/example.html index d02c8ef..a10aa1c 100644 --- a/example.html +++ b/example.html @@ -1,12 +1,12 @@ - - - + + +
diff --git a/package-lock.json b/package-lock.json index c11a65b..b4bbfbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1648,9 +1648,9 @@ } }, "@types/jest": { - "version": "26.0.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.4.tgz", - "integrity": "sha512-4fQNItvelbNA9+sFgU+fhJo8ZFF+AS4Egk3GWwCW2jFtViukXbnztccafAdLhzE/0EiCogljtQQXP8aQ9J7sFg==", + "version": "26.0.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.5.tgz", + "integrity": "sha512-heU+7w8snfwfjtcj2H458aTx3m5unIToOJhx75ebHilBiiQ39OIdA18WkG4LP08YKeAoWAGvWg8s+22w/PeJ6w==", "dev": true, "requires": { "jest-diff": "^25.2.1", @@ -14874,9 +14874,9 @@ } }, "ts-loader": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-7.0.5.tgz", - "integrity": "sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.1.tgz", + "integrity": "sha512-I9Nmly0ufJoZRMuAT9d5ijsC2B7oSPvUnOJt/GhgoATlPGYfa17VicDKPcqwUCrHpOkCxr/ybLYwbnS4cOxmvQ==", "dev": true, "requires": { "chalk": "^2.3.0", @@ -15073,9 +15073,9 @@ } }, "typescript": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", - "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index b16936e..2cdaef2 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,6 @@ { "pkgRoot": "/build" } - ], - "branches": [ - "main" ] }, "keywords": [ @@ -40,7 +37,7 @@ "license": "ISC", "devDependencies": { "@types/isomorphic-fetch": "0.0.35", - "@types/jest": "^26.0.4", + "@types/jest": "^26.0.5", "cp": "^0.2.0", "jest": "^25.5.4", "path": "^0.12.7", @@ -48,13 +45,13 @@ "rimraf": "^3.0.2", "semantic-release": "^17.1.1", "ts-jest": "^25.5.1", - "ts-loader": "^7.0.5", + "ts-loader": "^8.0.1", "tslint": "^6.1.2", "tslint-config-prettier": "^1.18.0", "typedoc": "^0.17.8", "typedoc-neo-theme": "^1.0.9", "typedoc-plugin-external-module-name": "^4.0.3", - "typescript": "^3.9.6", + "typescript": "^3.9.7", "uglifyjs-webpack-plugin": "^2.2.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.12" diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 3e75506..a5c2031 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -1378,11 +1378,51 @@ } } }, + "@sasjs/test-framework": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.3.0.tgz", + "integrity": "sha512-vrbRFUhNUShLlNFZO+XwVwFLXDLApQG9zOPx00xhQ8IUA0cSDFFmf2mP/KBdFCxa1REaR6GHvMctUj+xRZo9QQ==", + "requires": { + "@types/react-highlight.js": "^1.0.0", + "moment": "^2.27.0", + "react-highlight.js": "^1.0.7", + "semantic-ui-css": "^2.4.1", + "semantic-ui-react": "^1.0.0" + } + }, + "@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": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", "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": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", @@ -1858,6 +1898,14 @@ "@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": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", @@ -3699,6 +3747,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", @@ -4117,6 +4170,15 @@ "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": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -5641,6 +5703,11 @@ "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": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -6510,6 +6577,11 @@ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "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": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", @@ -6654,6 +6726,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "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": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -8006,6 +8083,11 @@ } } }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" + }, "js-base64": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz", @@ -8155,6 +8237,11 @@ "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -8919,6 +9006,11 @@ "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -9912,6 +10004,11 @@ "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": { "version": "1.0.26", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", @@ -11353,11 +11450,34 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", "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": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "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": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", @@ -12097,6 +12217,32 @@ "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.0.0", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.0.0.tgz", + "integrity": "sha512-85mYHYuDBNa6la1BgKwuOSD1vcIPsFQEXRxGsZ9pUtE4iHlEcylF+x46NYHIGbBjlys63SpNH3PtK6VyZj9LBw==", + "requires": { + "@babel/runtime": "^7.1.2", + "@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", + "classnames": "^2.2.6", + "keyboard-key": "^1.0.4", + "lodash": "^4.17.15", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "react-popper": "^1.3.4", + "shallowequal": "^1.1.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -12286,6 +12432,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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -13454,6 +13605,11 @@ "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": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -13755,6 +13911,14 @@ "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": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", diff --git a/sasjs-tests/package.json b/sasjs-tests/package.json index 29b9734..278075a 100644 --- a/sasjs-tests/package.json +++ b/sasjs-tests/package.json @@ -5,6 +5,7 @@ "private": true, "dependencies": { "@sasjs/adapter": "^1.0.5", + "@sasjs/test-framework": "^1.3.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", diff --git a/sasjs-tests/src/App.scss b/sasjs-tests/src/App.scss deleted file mode 100644 index a61d835..0000000 --- a/sasjs-tests/src/App.scss +++ /dev/null @@ -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%; - } -} diff --git a/sasjs-tests/src/App.tsx b/sasjs-tests/src/App.tsx index 4ede90b..a7ee15e 100644 --- a/sasjs-tests/src/App.tsx +++ b/sasjs-tests/src/App.tsx @@ -1,56 +1,30 @@ import React, { ReactElement, useState, useContext, useEffect } from "react"; -import "./App.scss"; -import TestSuiteRunner from "./TestSuiteRunner"; -import { AppContext } from "./context/AppContext"; +import { TestSuiteRunner, TestSuite, AppContext } from "@sasjs/test-framework"; +import { basicTests } from "./testSuites/Basic"; +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 [appLoc, setAppLoc] = useState(""); - const [debug, setDebug] = useState(false); - const { adapter } = useContext(AppContext); + const { adapter, config } = useContext(AppContext); + const [testSuites, setTestSuites] = useState([]); useEffect(() => { if (adapter) { - adapter.setDebugState(debug); + setTestSuites([ + basicTests(adapter, config.userName, config.password), + sendArrTests(adapter), + sendObjTests(adapter), + specialCaseTests(adapter), + sasjsRequestTests(adapter), + ]); } - }, [debug, adapter]); - - useEffect(() => { - if (appLoc && adapter) { - adapter.setSASjsConfig({ ...adapter.getSasjsConfig(), appLoc }); - } - }, [appLoc, adapter]); - - useEffect(() => { - setAppLoc(adapter.getSasjsConfig().appLoc); - }, [adapter]); + }, [adapter, config]); return (
-
-
- -
- -
-
-
- - setAppLoc(e.target.value)} - placeholder="AppLoc" - /> -
-
- {adapter && } + {adapter && testSuites && }
); }; diff --git a/sasjs-tests/src/Login.tsx b/sasjs-tests/src/Login.tsx index 38f05f2..cd0fac4 100644 --- a/sasjs-tests/src/Login.tsx +++ b/sasjs-tests/src/Login.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, useState, useCallback, useContext } from "react"; import "./Login.scss"; -import { AppContext } from "./context/AppContext"; +import { AppContext } from "@sasjs/test-framework"; import { Redirect } from "react-router-dom"; const Login = (): ReactElement<{}> => { diff --git a/sasjs-tests/src/PrivateRoute.tsx b/sasjs-tests/src/PrivateRoute.tsx index a70d282..8420955 100644 --- a/sasjs-tests/src/PrivateRoute.tsx +++ b/sasjs-tests/src/PrivateRoute.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, useContext, FunctionComponent } from "react"; import { Redirect, Route } from "react-router-dom"; -import { AppContext } from "./context/AppContext"; +import { AppContext } from "@sasjs/test-framework"; interface PrivateRouteProps { component: FunctionComponent; diff --git a/sasjs-tests/src/TestSuiteRunner.scss b/sasjs-tests/src/TestSuiteRunner.scss deleted file mode 100644 index 880b9db..0000000 --- a/sasjs-tests/src/TestSuiteRunner.scss +++ /dev/null @@ -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; - } -} diff --git a/sasjs-tests/src/TestSuiteRunner.tsx b/sasjs-tests/src/TestSuiteRunner.tsx deleted file mode 100644 index 449371b..0000000 --- a/sasjs-tests/src/TestSuiteRunner.tsx +++ /dev/null @@ -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/adapter"; -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 => { - const { adapter } = props; - const { config } = useContext(AppContext); - const [testSuites, setTestSuites] = useState([]); - 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( - (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 ( - <> -
- -
- {completedTestSuites.map((completedTestSuite, index) => { - return ( - - ); - })} - {currentTestSuite && runTests && ( - { - 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; diff --git a/sasjs-tests/src/components/Test.tsx b/sasjs-tests/src/components/Test.tsx deleted file mode 100644 index 6d13850..0000000 --- a/sasjs-tests/src/components/Test.tsx +++ /dev/null @@ -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; - afterTest?: (...args: any) => Promise; - test: (context: any) => Promise; - 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 => { - 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 ( - - ); -}; - -export default Test; diff --git a/sasjs-tests/src/components/TestCard.scss b/sasjs-tests/src/components/TestCard.scss deleted file mode 100644 index 306d078..0000000 --- a/sasjs-tests/src/components/TestCard.scss +++ /dev/null @@ -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%; - } -} diff --git a/sasjs-tests/src/components/TestCard.tsx b/sasjs-tests/src/components/TestCard.tsx deleted file mode 100644 index 404f39c..0000000 --- a/sasjs-tests/src/components/TestCard.tsx +++ /dev/null @@ -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 => { - const { title, description, status, error, executionTime } = props; - - return ( -
- {title} - {description} - - {executionTime ? executionTime.toFixed(2) + "s" : ""} - - {status === "running" && ( -
- Running... -
- )} - {status === "passed" && ( -
- Passed -
- )} - {status === "failed" && ( - <> -
- Failed -
- {!!error && {error.message}} - - )} -
- ); -}; - -export default TestCard; diff --git a/sasjs-tests/src/components/TestSuite.tsx b/sasjs-tests/src/components/TestSuite.tsx deleted file mode 100644 index 850d141..0000000 --- a/sasjs-tests/src/components/TestSuite.tsx +++ /dev/null @@ -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; - afterAll?: (...args: any) => Promise; - onCompleted: ( - name: string, - completedTests: { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[] - ) => void; -} -const TestSuite = (props: TestSuiteProps): ReactElement => { - const { name, tests, beforeAll, afterAll, onCompleted } = props; - const [context, setContext] = useState(null); - const [completedTests, setCompletedTests] = useState< - { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[] - >([]); - const [currentTest, setCurrentTest] = useState( - (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 ? ( -
-
{name}
- {currentTest && ( - { - 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 ( - - ); - })} -
- ) : ( - <> - ); -}; - -export default TestSuite; diff --git a/sasjs-tests/src/components/TestSuiteCard.scss b/sasjs-tests/src/components/TestSuiteCard.scss deleted file mode 100644 index 9ce7289..0000000 --- a/sasjs-tests/src/components/TestSuiteCard.scss +++ /dev/null @@ -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; - } - } -} diff --git a/sasjs-tests/src/components/TestSuiteCard.tsx b/sasjs-tests/src/components/TestSuiteCard.tsx deleted file mode 100644 index c573e03..0000000 --- a/sasjs-tests/src/components/TestSuiteCard.tsx +++ /dev/null @@ -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 => { - const { name, tests } = props; - const overallStatus = tests.map((t) => t.result).reduce((x, y) => x && y); - - return ( -
-
- {name} -
- {tests.map((completedTest, index) => { - const { test, result, error, executionTime } = completedTest; - const { title, description } = test; - return ( - - ); - })} -
- ); -}; - -export default TestSuiteCard; diff --git a/sasjs-tests/src/context/AppContext.tsx b/sasjs-tests/src/context/AppContext.tsx deleted file mode 100644 index 6a20af3..0000000 --- a/sasjs-tests/src/context/AppContext.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { createContext, useState, useEffect, ReactNode } from "react"; -import SASjs from "@sasjs/adapter"; - -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((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 ( - - {props.children} - - ); -}; diff --git a/sasjs-tests/src/index.tsx b/sasjs-tests/src/index.tsx index 48791c8..effc537 100644 --- a/sasjs-tests/src/index.tsx +++ b/sasjs-tests/src/index.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom"; import { Route, HashRouter, Switch } from "react-router-dom"; import "./index.scss"; import * as serviceWorker from "./serviceWorker"; -import { AppProvider } from "./context/AppContext"; +import { AppProvider } from "@sasjs/test-framework"; import PrivateRoute from "./PrivateRoute"; import Login from "./Login"; import App from "./App"; diff --git a/sasjs-tests/src/testSuites/Basic.ts b/sasjs-tests/src/testSuites/Basic.ts index ee0be3d..903f11b 100644 --- a/sasjs-tests/src/testSuites/Basic.ts +++ b/sasjs-tests/src/testSuites/Basic.ts @@ -1,5 +1,5 @@ import SASjs, { ServerType, SASjsConfig } from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const defaultConfig: SASjsConfig = { serverUrl: window.location.origin, diff --git a/sasjs-tests/src/testSuites/RequestData.ts b/sasjs-tests/src/testSuites/RequestData.ts index 2ed3672..0f4b82d 100644 --- a/sasjs-tests/src/testSuites/RequestData.ts +++ b/sasjs-tests/src/testSuites/RequestData.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const stringData: any = { table1: [{ col1: "first col value" }] }; const numericData: any = { table1: [{ col1: 3.14159265 }] }; diff --git a/sasjs-tests/src/testSuites/SasjsRequests.ts b/sasjs-tests/src/testSuites/SasjsRequests.ts index c17f2d0..9a4a5e5 100644 --- a/sasjs-tests/src/testSuites/SasjsRequests.ts +++ b/sasjs-tests/src/testSuites/SasjsRequests.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const data: any = { table1: [{ col1: "first col value" }] }; diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 6117805..0785204 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const specialCharData: any = { table1: [ diff --git a/sasjs-tests/src/types/index.ts b/sasjs-tests/src/types/index.ts deleted file mode 100644 index 832aca1..0000000 --- a/sasjs-tests/src/types/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface Test { - title: string; - description: string; - beforeTest?: (...args: any) => Promise; - afterTest?: (...args: any) => Promise; - test: (context?: any) => Promise; - assertion: (...args: any) => boolean; -} - -export interface TestSuite { - name: string; - tests: Test[]; - beforeAll?: (...args: any) => Promise; - afterAll?: (...args: any) => Promise; -} diff --git a/sasjs-tests/src/utils/UploadFile.ts b/sasjs-tests/src/utils/UploadFile.ts deleted file mode 100644 index 5dafccd..0000000 --- a/sasjs-tests/src/utils/UploadFile.ts +++ /dev/null @@ -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); - }); -};