From 6e8b19eda1b0c9b856b5779ec874123b7a53f4f5 Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 1 Mar 2022 15:32:30 +0100 Subject: [PATCH 1/7] chore: special missings, test assertion improved, added failing test --- package-lock.json | 160 ++++++++++----- package.json | 2 +- sasjs-tests/README.md | 8 +- sasjs-tests/package-lock.json | 219 +++++++++++++-------- sasjs-tests/src/testSuites/SpecialCases.ts | 79 +++++--- 5 files changed, 301 insertions(+), 167 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4925868..aa17646 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "hasInstallScript": true, "license": "ISC", "dependencies": { - "@sasjs/utils": "2.35.0", + "@sasjs/utils": "2.36.1", "axios": "0.26.0", "axios-cookiejar-support": "1.0.1", "form-data": "4.0.0", @@ -1143,23 +1143,50 @@ } }, "node_modules/@sasjs/utils": { - "version": "2.35.0", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz", - "integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==", + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz", + "integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==", "hasInstallScript": true, "dependencies": { - "@types/fs-extra": "^9.0.13", - "@types/prompts": "^2.0.13", - "chalk": "^4.1.1", - "cli-table": "^0.3.6", - "consola": "^2.15.0", - "csv-stringify": "^5.6.5", + "@types/fs-extra": "9.0.13", + "@types/prompts": "2.0.13", + "chalk": "4.1.1", + "cli-table": "0.3.6", + "consola": "2.15.0", + "csv-stringify": "5.6.5", "find": "0.3.0", - "fs-extra": "^10.0.0", - "jwt-decode": "^3.1.2", - "prompts": "^2.4.1", - "rimraf": "^3.0.2", - "valid-url": "^1.0.9" + "fs-extra": "10.0.0", + "jwt-decode": "3.1.2", + "prompts": "2.4.1", + "rimraf": "3.0.2", + "valid-url": "1.0.9" + } + }, + "node_modules/@sasjs/utils/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@sasjs/utils/node_modules/prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, "node_modules/@semantic-release/commit-analyzer": { @@ -1644,9 +1671,9 @@ "dev": true }, "node_modules/@types/prompts": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.14.tgz", - "integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "dependencies": { "@types/node": "*" } @@ -2068,6 +2095,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2692,6 +2720,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2762,12 +2791,14 @@ } }, "node_modules/cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", + "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", "dependencies": { - "colors": "1.0.3", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" } }, "node_modules/cli-table3": { @@ -2912,9 +2943,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==" }, "node_modules/console-browserify": { "version": "1.2.0", @@ -10539,6 +10570,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -11685,6 +11717,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14018,22 +14051,42 @@ } }, "@sasjs/utils": { - "version": "2.35.0", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz", - "integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==", + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz", + "integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==", "requires": { - "@types/fs-extra": "^9.0.13", - "@types/prompts": "^2.0.13", - "chalk": "^4.1.1", - "cli-table": "^0.3.6", - "consola": "^2.15.0", - "csv-stringify": "^5.6.5", + "@types/fs-extra": "9.0.13", + "@types/prompts": "2.0.13", + "chalk": "4.1.1", + "cli-table": "0.3.6", + "consola": "2.15.0", + "csv-stringify": "5.6.5", "find": "0.3.0", - "fs-extra": "^10.0.0", - "jwt-decode": "^3.1.2", - "prompts": "^2.4.1", - "rimraf": "^3.0.2", - "valid-url": "^1.0.9" + "fs-extra": "10.0.0", + "jwt-decode": "3.1.2", + "prompts": "2.4.1", + "rimraf": "3.0.2", + "valid-url": "1.0.9" + }, + "dependencies": { + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + } } }, "@semantic-release/commit-analyzer": { @@ -14458,9 +14511,9 @@ "dev": true }, "@types/prompts": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.14.tgz", - "integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "requires": { "@types/node": "*" } @@ -14828,7 +14881,8 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "ansi-styles": { "version": "4.3.0", @@ -15312,6 +15366,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -15364,12 +15419,11 @@ "dev": true }, "cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", + "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", "requires": { - "colors": "1.0.3", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" } }, "cli-table3": { @@ -15491,9 +15545,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==" }, "console-browserify": { "version": "1.2.0", @@ -21233,6 +21287,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -22135,6 +22190,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } diff --git a/package.json b/package.json index cb8e823..667669c 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ }, "main": "index.js", "dependencies": { - "@sasjs/utils": "2.35.0", + "@sasjs/utils": "2.36.1", "axios": "0.26.0", "axios-cookiejar-support": "1.0.1", "form-data": "4.0.0", diff --git a/sasjs-tests/README.md b/sasjs-tests/README.md index 53b0a5f..573ae53 100644 --- a/sasjs-tests/README.md +++ b/sasjs-tests/README.md @@ -70,7 +70,7 @@ parmcards4; %webout(FETCH) %webout(OPEN) %macro x(); - %do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i,missing=STRING) %end; + %do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i,missing=STRING,showmeta=YES) %end; %mend; %x() %webout(CLOSE) ;;;; @@ -79,7 +79,7 @@ parmcards4; %webout(FETCH) %webout(OPEN) %macro x(); - %do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i,missing=STRING) %end; + %do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i,missing=STRING,showmeta=YES) %end; %mend; %x() %webout(CLOSE) ;;;; @@ -111,7 +111,7 @@ parmcards4; %macro x(); %do i=1 %to %sysfunc(countw(&sasjs_tables)); %let table=%scan(&sasjs_tables,&i); - %webout(OBJ,&table,missing=STRING) + %webout(OBJ,&table,missing=STRING,showmeta=YES) %end; %mend; %x() @@ -125,7 +125,7 @@ parmcards4; %macro x(); %do i=1 %to %sysfunc(countw(&sasjs_tables)); %let table=%scan(&sasjs_tables,&i); - %webout(ARR,&table,missing=STRING) + %webout(ARR,&table,missing=STRING,showmeta=YES) %end; %mend; %x() diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 9d6d4fa..cb6e5e7 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -2388,16 +2388,16 @@ "node_modules/@sasjs/adapter": { "version": "5.0.0", "resolved": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-O9BBQCqMR7l1fsGPD+onh1ET93ZCuIr8sJMA7Y8sOm8JXigUooDtdk/Lo6emBT7Ibwu1kR3gW88oeL+jN3kH/w==", + "integrity": "sha512-iPr8TRKHAFSI9tWJjCqB/M+DzstyhQSJhrovkhqiKB76tuqck+vvgTiB+4KoHEq6PjlbW+mfIzHcVC9trrztxA==", "hasInstallScript": true, "license": "ISC", "dependencies": { - "@sasjs/utils": "^2.32.0", - "axios": "^0.21.4", - "axios-cookiejar-support": "^1.0.1", - "form-data": "^4.0.0", - "https": "^1.0.0", - "tough-cookie": "^4.0.0" + "@sasjs/utils": "2.36.1", + "axios": "0.26.0", + "axios-cookiejar-support": "1.0.1", + "form-data": "4.0.0", + "https": "1.0.0", + "tough-cookie": "4.0.0" } }, "node_modules/@sasjs/test-framework": { @@ -2422,24 +2422,50 @@ } }, "node_modules/@sasjs/utils": { - "version": "2.34.1", - "integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==", + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz", + "integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==", "hasInstallScript": true, "dependencies": { - "@types/fs-extra": "^9.0.11", - "@types/prompts": "^2.0.13", - "chalk": "^4.1.1", - "cli-table": "^0.3.6", - "consola": "^2.15.0", - "csv-stringify": "^5.6.5", + "@types/fs-extra": "9.0.13", + "@types/prompts": "2.0.13", + "chalk": "4.1.1", + "cli-table": "0.3.6", + "consola": "2.15.0", + "csv-stringify": "5.6.5", "find": "0.3.0", - "fs-extra": "^10.0.0", - "jwt-decode": "^3.1.2", - "lodash.groupby": "4.6.0", - "lodash.uniqby": "4.7.0", - "prompts": "^2.4.1", - "rimraf": "^3.0.2", - "valid-url": "^1.0.9" + "fs-extra": "10.0.0", + "jwt-decode": "3.1.2", + "prompts": "2.4.1", + "rimraf": "3.0.2", + "valid-url": "1.0.9" + } + }, + "node_modules/@sasjs/utils/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@sasjs/utils/node_modules/prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, "node_modules/@semantic-ui-react/event-stack": { @@ -2723,6 +2749,7 @@ }, "node_modules/@types/fs-extra": { "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dependencies": { "@types/node": "*" @@ -2811,8 +2838,9 @@ "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==" }, "node_modules/@types/prompts": { - "version": "2.0.14", - "integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "dependencies": { "@types/node": "*" } @@ -3751,10 +3779,11 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "node_modules/axios-cookiejar-support": { @@ -4834,8 +4863,9 @@ } }, "node_modules/cli-table": { - "version": "0.3.11", - "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", + "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", "dependencies": { "colors": "1.0.3" }, @@ -5002,6 +5032,7 @@ }, "node_modules/colors": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "engines": { "node": ">=0.1.90" @@ -5112,8 +5143,9 @@ } }, "node_modules/consola": { - "version": "2.15.3", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==" }, "node_modules/console-browserify": { "version": "1.2.0", @@ -5705,6 +5737,7 @@ }, "node_modules/csv-stringify": { "version": "5.6.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" }, "node_modules/cyclist": { @@ -7786,6 +7819,7 @@ }, "node_modules/find": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", "dependencies": { "traverse-chain": "~0.1.0" @@ -7843,8 +7877,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.6", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", "funding": [ { "type": "individual", @@ -8114,6 +8149,7 @@ }, "node_modules/fs-extra": { "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", "dependencies": { "graceful-fs": "^4.2.0", @@ -10633,6 +10669,7 @@ }, "node_modules/jwt-decode": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, "node_modules/keyboard-key": { @@ -10750,10 +10787,6 @@ "version": "4.0.8", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "node_modules/lodash.groupby": { - "version": "4.6.0", - "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" @@ -10785,10 +10818,6 @@ "version": "4.5.0", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=" - }, "node_modules/loglevel": { "version": "1.8.0", "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", @@ -17128,6 +17157,7 @@ }, "node_modules/traverse-chain": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=" }, "node_modules/trim-newlines": { @@ -17619,6 +17649,7 @@ }, "node_modules/valid-url": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=" }, "node_modules/validate-npm-package-license": { @@ -20967,14 +20998,14 @@ }, "@sasjs/adapter": { "version": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-O9BBQCqMR7l1fsGPD+onh1ET93ZCuIr8sJMA7Y8sOm8JXigUooDtdk/Lo6emBT7Ibwu1kR3gW88oeL+jN3kH/w==", + "integrity": "sha512-iPr8TRKHAFSI9tWJjCqB/M+DzstyhQSJhrovkhqiKB76tuqck+vvgTiB+4KoHEq6PjlbW+mfIzHcVC9trrztxA==", "requires": { - "@sasjs/utils": "^2.32.0", - "axios": "^0.21.4", - "axios-cookiejar-support": "^1.0.1", - "form-data": "^4.0.0", - "https": "^1.0.0", - "tough-cookie": "^4.0.0" + "@sasjs/utils": "2.36.1", + "axios": "0.26.0", + "axios-cookiejar-support": "1.0.1", + "form-data": "4.0.0", + "https": "1.0.0", + "tough-cookie": "4.0.0" } }, "@sasjs/test-framework": { @@ -20991,23 +21022,42 @@ } }, "@sasjs/utils": { - "version": "2.34.1", - "integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==", + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz", + "integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==", "requires": { - "@types/fs-extra": "^9.0.11", - "@types/prompts": "^2.0.13", - "chalk": "^4.1.1", - "cli-table": "^0.3.6", - "consola": "^2.15.0", - "csv-stringify": "^5.6.5", + "@types/fs-extra": "9.0.13", + "@types/prompts": "2.0.13", + "chalk": "4.1.1", + "cli-table": "0.3.6", + "consola": "2.15.0", + "csv-stringify": "5.6.5", "find": "0.3.0", - "fs-extra": "^10.0.0", - "jwt-decode": "^3.1.2", - "lodash.groupby": "4.6.0", - "lodash.uniqby": "4.7.0", - "prompts": "^2.4.1", - "rimraf": "^3.0.2", - "valid-url": "^1.0.9" + "fs-extra": "10.0.0", + "jwt-decode": "3.1.2", + "prompts": "2.4.1", + "rimraf": "3.0.2", + "valid-url": "1.0.9" + }, + "dependencies": { + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + } } }, "@semantic-ui-react/event-stack": { @@ -21186,6 +21236,7 @@ }, "@types/fs-extra": { "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "requires": { "@types/node": "*" @@ -21274,8 +21325,9 @@ "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==" }, "@types/prompts": { - "version": "2.0.14", - "integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "requires": { "@types/node": "*" } @@ -21992,10 +22044,11 @@ "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==" }, "axios": { - "version": "0.21.4", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "axios-cookiejar-support": { @@ -22832,8 +22885,9 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, "cli-table": { - "version": "0.3.11", - "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", + "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", "requires": { "colors": "1.0.3" } @@ -22967,6 +23021,7 @@ }, "colors": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" }, "combined-stream": { @@ -23055,8 +23110,9 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" }, "consola": { - "version": "2.15.3", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==" }, "console-browserify": { "version": "1.2.0", @@ -23501,6 +23557,7 @@ }, "csv-stringify": { "version": "5.6.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" }, "cyclist": { @@ -25029,6 +25086,7 @@ }, "find": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", "requires": { "traverse-chain": "~0.1.0" @@ -25076,8 +25134,9 @@ } }, "follow-redirects": { - "version": "1.14.6", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, "for-in": { "version": "1.0.2", @@ -25274,6 +25333,7 @@ }, "fs-extra": { "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", "requires": { "graceful-fs": "^4.2.0", @@ -27139,6 +27199,7 @@ }, "jwt-decode": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, "keyboard-key": { @@ -27232,10 +27293,6 @@ "version": "4.0.8", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "lodash.groupby": { - "version": "4.6.0", - "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=" - }, "lodash.memoize": { "version": "4.1.2", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" @@ -27267,10 +27324,6 @@ "version": "4.5.0", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, - "lodash.uniqby": { - "version": "4.7.0", - "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=" - }, "loglevel": { "version": "1.8.0", "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==" @@ -32152,6 +32205,7 @@ }, "traverse-chain": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=" }, "trim-newlines": { @@ -32524,6 +32578,7 @@ }, "valid-url": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=" }, "validate-npm-package-license": { diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 2bf9c88..68b922e 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -80,16 +80,22 @@ const errorAndCsrfData: any = { } const testTable = 'sometable' -export const testTableWithNullVars: { [key: string]: any } = { +export const testTableWithSpecialNumeric: { [key: string]: any } = { [testTable]: [ - { var1: 'string', var2: 232, nullvar: 'A' }, - { var1: 'string', var2: 232, nullvar: 'B' }, - { var1: 'string', var2: 232, nullvar: '_' }, - { var1: 'string', var2: 232, nullvar: 0 }, - { var1: 'string', var2: 232, nullvar: 'z' }, - { var1: 'string', var2: 232, nullvar: null } + { var1: 'string', var2: 232, specialnumeric: 'A' }, + { var1: 'string', var2: 232, specialnumeric: 'B' }, + { var1: 'string', var2: 232, specialnumeric: '_' }, + { var1: 'string', var2: 232, specialnumeric: 0 }, + { var1: 'string', var2: 232, specialnumeric: 'z' }, + { var1: 'string', var2: 232, specialnumeric: null } ], - [`$${testTable}`]: { formats: { var1: '$char12.', nullvar: 'best.' } } + [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } +} +export const testTableWithSpecialNumericOneRow: { [key: string]: any } = { + [testTable]: [ + { var1: 'string', var2: 232, specialnumeric: 'S' } + ], + [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } } export const specialCaseTests = (adapter: SASjs): TestSuite => ({ @@ -265,31 +271,48 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ title: 'Special missing values', description: 'Should support special missing values', test: () => { - return adapter.request('common/sendObj', testTableWithNullVars) + return adapter.request('common/sendObj', testTableWithSpecialNumeric) }, assertion: (res: any) => { let assertionRes = true - testTableWithNullVars[testTable].forEach( - (row: { [key: string]: any }, i: number) => - Object.keys(row).forEach((col: string) => { - const resValue = res[testTable][i][col.toUpperCase()] + //We check the $ formats values that come from sas are in right order + const resVars = res[`$${testTable}`].vars - if ( - typeof row[col] === 'string' && - testTableWithNullVars[`$${testTable}`].formats[col] === - 'best.' && - row[col].toUpperCase() !== resValue - ) { - assertionRes = false - } else if ( - typeof row[col] !== 'string' && - row[col] !== resValue - ) { - assertionRes = false - } - }) - ) + Object.keys(resVars).forEach( + (key: any, i: number) => { + let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] + //If it is char, we change id to $ to pass the test + //If it is number, it will already be compatible to comapre + formatValue = formatValue?.includes('$') ? '$' : formatValue + + if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + }) + + return assertionRes + } + }, + { + title: 'Special missing values (ONE ROW)', + description: 'Should support special missing values, when one row is send', + test: () => { + return adapter.request('common/sendObj', testTableWithSpecialNumericOneRow,) + }, + assertion: (res: any) => { + let assertionRes = true + + //We check the $ formats values that come from sas are in right order + const resVars = res[`$${testTable}`].vars + + Object.keys(resVars).forEach( + (key: any, i: number) => { + let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] + //If it is char, we change id to $ to pass the test + //If it is number, it will already be compatible to comapre + formatValue = formatValue?.includes('$') ? '$' : formatValue + + if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + }) return assertionRes } From 888a2b9bd3b0f7a39b6850bbc555455499008e16 Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 1 Mar 2022 17:17:11 +0100 Subject: [PATCH 2/7] chore: test special missings with lowercase value --- sasjs-tests/src/testSuites/SpecialCases.ts | 79 ++++++++++++++++++++-- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 68b922e..f2a12a4 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -86,7 +86,7 @@ export const testTableWithSpecialNumeric: { [key: string]: any } = { { var1: 'string', var2: 232, specialnumeric: 'B' }, { var1: 'string', var2: 232, specialnumeric: '_' }, { var1: 'string', var2: 232, specialnumeric: 0 }, - { var1: 'string', var2: 232, specialnumeric: 'z' }, + { var1: 'string', var2: 232, specialnumeric: 'Z' }, { var1: 'string', var2: 232, specialnumeric: null } ], [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } @@ -97,6 +97,12 @@ export const testTableWithSpecialNumericOneRow: { [key: string]: any } = { ], [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } } +export const testTableWithSpecialNumericLowercase: { [key: string]: any } = { + [testTable]: [ + { var1: 'string', var2: 232, specialnumeric: 's' } + ], + [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } +} export const specialCaseTests = (adapter: SASjs): TestSuite => ({ name: 'Special Cases', @@ -276,19 +282,29 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ assertion: (res: any) => { let assertionRes = true - //We check the $ formats values that come from sas are in right order + //We receive formats in response. We compare it with formats that we included in request to make sure they are equal const resVars = res[`$${testTable}`].vars Object.keys(resVars).forEach( (key: any, i: number) => { let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] - //If it is char, we change id to $ to pass the test - //If it is number, it will already be compatible to comapre + //If it is char, we change it to $ to be compatible for comparsion + //If it is number, it will already be compatible to comapre (best.) formatValue = formatValue?.includes('$') ? '$' : formatValue if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false }) + //Here we will compare the response values with values we send + const resVals = res[testTable] + + testTableWithSpecialNumeric[testTable].forEach( + (row: { [key: string]: any }, i: number) => + Object.keys(row).forEach((col: string) => { + if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + }) + ) + return assertionRes } }, @@ -301,19 +317,68 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ assertion: (res: any) => { let assertionRes = true - //We check the $ formats values that come from sas are in right order + //We receive formats in response. We compare it with formats that we included in request to make sure they are equal const resVars = res[`$${testTable}`].vars Object.keys(resVars).forEach( (key: any, i: number) => { let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] - //If it is char, we change id to $ to pass the test - //If it is number, it will already be compatible to comapre + //If it is char, we change it to $ to be compatible for comparsion + //If it is number, it will already be compatible to comapre (best.) formatValue = formatValue?.includes('$') ? '$' : formatValue if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false }) + //Here we will compare the response values with values we send + const resVals = res[testTable] + + testTableWithSpecialNumericOneRow[testTable].forEach( + (row: { [key: string]: any }, i: number) => + Object.keys(row).forEach((col: string) => { + if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + }) + ) + + return assertionRes + } + }, + { + title: 'Special missing values (LOWERCASE)', + description: 'Should support special missing values, when LOWERCASE value is send', + test: () => { + return adapter.request('common/sendObj', testTableWithSpecialNumericLowercase,) + }, + assertion: (res: any) => { + let assertionRes = true + + //We receive formats in response. We compare it with formats that we included in request to make sure they are equal + const resVars = res[`$${testTable}`].vars + + Object.keys(resVars).forEach( + (key: any, i: number) => { + let formatValue = testTableWithSpecialNumericLowercase[`$${testTable}`].formats[key.toLowerCase()] + //If it is char, we change it to $ to be compatible for comparsion + //If it is number, it will already be compatible to comapre (best.) + formatValue = formatValue?.includes('$') ? '$' : formatValue + + if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + }) + + //Here we will compare the response values with values we send + const resVals = res[testTable] + + testTableWithSpecialNumericLowercase[testTable].forEach( + (row: { [key: string]: any }, i: number) => + Object.keys(row).forEach((col: string) => { + if (col === 'specialnumeric') { + if (resVals[i][col.toUpperCase()] !== row[col].toUpperCase()) assertionRes = false + } else { + if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + } + }) + ) + return assertionRes } } From f0f33cee52004e2853a7cf0dc841577ce710ee37 Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 3 Mar 2022 10:58:51 +0300 Subject: [PATCH 3/7] chore(sasjs-tests): improve spelling and code readability --- sasjs-tests/package-lock.json | 4 +- sasjs-tests/src/testSuites/SpecialCases.ts | 123 +++++++++++++-------- 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index cb6e5e7..53400d6 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -2388,7 +2388,7 @@ "node_modules/@sasjs/adapter": { "version": "5.0.0", "resolved": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-iPr8TRKHAFSI9tWJjCqB/M+DzstyhQSJhrovkhqiKB76tuqck+vvgTiB+4KoHEq6PjlbW+mfIzHcVC9trrztxA==", + "integrity": "sha512-JZsvovkPNb0dlnBRh1uDdvgJg+BE0ebIomWwhYjHj3YApds4MgI++1pa+Ck2tltIUUYASeiPUIPJJX8tF3VCTw==", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -20998,7 +20998,7 @@ }, "@sasjs/adapter": { "version": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-iPr8TRKHAFSI9tWJjCqB/M+DzstyhQSJhrovkhqiKB76tuqck+vvgTiB+4KoHEq6PjlbW+mfIzHcVC9trrztxA==", + "integrity": "sha512-JZsvovkPNb0dlnBRh1uDdvgJg+BE0ebIomWwhYjHj3YApds4MgI++1pa+Ck2tltIUUYASeiPUIPJJX8tF3VCTw==", "requires": { "@sasjs/utils": "2.36.1", "axios": "0.26.0", diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index f2a12a4..2b77581 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -92,15 +92,11 @@ export const testTableWithSpecialNumeric: { [key: string]: any } = { [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } } export const testTableWithSpecialNumericOneRow: { [key: string]: any } = { - [testTable]: [ - { var1: 'string', var2: 232, specialnumeric: 'S' } - ], + [testTable]: [{ var1: 'string', var2: 232, specialnumeric: 'S' }], [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } } export const testTableWithSpecialNumericLowercase: { [key: string]: any } = { - [testTable]: [ - { var1: 'string', var2: 232, specialnumeric: 's' } - ], + [testTable]: [{ var1: 'string', var2: 232, specialnumeric: 's' }], [`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } } } @@ -282,26 +278,35 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ assertion: (res: any) => { let assertionRes = true - //We receive formats in response. We compare it with formats that we included in request to make sure they are equal + // We receive formats in response. We compare it with formats that we included in request to make sure they are equal const resVars = res[`$${testTable}`].vars - Object.keys(resVars).forEach( - (key: any, i: number) => { - let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] - //If it is char, we change it to $ to be compatible for comparsion - //If it is number, it will already be compatible to comapre (best.) - formatValue = formatValue?.includes('$') ? '$' : formatValue + Object.keys(resVars).forEach((key: any, i: number) => { + let formatValue = + testTableWithSpecialNumeric[`$${testTable}`].formats[ + key.toLowerCase() + ] + // If it is char, we change it to $ to be compatible for comparsion + // If it is number, it will already be compatible to comapre (best.) + formatValue = formatValue?.includes('$') ? '$' : formatValue - if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + if ( + formatValue !== undefined && + !resVars[key].format.includes(formatValue) + ) { + assertionRes = false + } }) - //Here we will compare the response values with values we send - const resVals = res[testTable] + // Here we will compare the response values with values we send + const resValues = res[testTable] testTableWithSpecialNumeric[testTable].forEach( (row: { [key: string]: any }, i: number) => Object.keys(row).forEach((col: string) => { - if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + if (resValues[i][col.toUpperCase()] !== row[col]) { + assertionRes = false + } }) ) @@ -310,33 +315,46 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ }, { title: 'Special missing values (ONE ROW)', - description: 'Should support special missing values, when one row is send', + description: + 'Should support special missing values, when one row is send', test: () => { - return adapter.request('common/sendObj', testTableWithSpecialNumericOneRow,) + return adapter.request( + 'common/sendObj', + testTableWithSpecialNumericOneRow + ) }, assertion: (res: any) => { let assertionRes = true - //We receive formats in response. We compare it with formats that we included in request to make sure they are equal + // We receive formats in response. We compare it with formats that we included in request to make sure they are equal const resVars = res[`$${testTable}`].vars - Object.keys(resVars).forEach( - (key: any, i: number) => { - let formatValue = testTableWithSpecialNumeric[`$${testTable}`].formats[key.toLowerCase()] - //If it is char, we change it to $ to be compatible for comparsion - //If it is number, it will already be compatible to comapre (best.) - formatValue = formatValue?.includes('$') ? '$' : formatValue + Object.keys(resVars).forEach((key: any, i: number) => { + let formatValue = + testTableWithSpecialNumeric[`$${testTable}`].formats[ + key.toLowerCase() + ] + // If it is char, we change it to $ to be compatible for comparsion + // If it is number, it will already be compatible to comapre (best.) + formatValue = formatValue?.includes('$') ? '$' : formatValue - if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + if ( + formatValue !== undefined && + !resVars[key].format.includes(formatValue) + ) { + assertionRes = false + } }) - //Here we will compare the response values with values we send - const resVals = res[testTable] + // Here we will compare the response values with values we send + const resValues = res[testTable] testTableWithSpecialNumericOneRow[testTable].forEach( (row: { [key: string]: any }, i: number) => Object.keys(row).forEach((col: string) => { - if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + if (resValues[i][col.toUpperCase()] !== row[col]) { + assertionRes = false + } }) ) @@ -345,36 +363,53 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ }, { title: 'Special missing values (LOWERCASE)', - description: 'Should support special missing values, when LOWERCASE value is send', + description: + 'Should support special missing values, when LOWERCASE value is sent', test: () => { - return adapter.request('common/sendObj', testTableWithSpecialNumericLowercase,) + return adapter.request( + 'common/sendObj', + testTableWithSpecialNumericLowercase + ) }, assertion: (res: any) => { let assertionRes = true - //We receive formats in response. We compare it with formats that we included in request to make sure they are equal + // We receive formats in response. We compare it with formats that we included in request to make sure they are equal const resVars = res[`$${testTable}`].vars - Object.keys(resVars).forEach( - (key: any, i: number) => { - let formatValue = testTableWithSpecialNumericLowercase[`$${testTable}`].formats[key.toLowerCase()] - //If it is char, we change it to $ to be compatible for comparsion - //If it is number, it will already be compatible to comapre (best.) - formatValue = formatValue?.includes('$') ? '$' : formatValue + Object.keys(resVars).forEach((key: any, i: number) => { + let formatValue = + testTableWithSpecialNumericLowercase[`$${testTable}`].formats[ + key.toLowerCase() + ] + // If it is a char, we change it to $ to be compatible for comparison + // If it is a number, it will already be compatible to compare (best.) + formatValue = formatValue?.includes('$') ? '$' : formatValue - if (formatValue !== undefined && !resVars[key].format.includes(formatValue)) assertionRes = false + if ( + formatValue !== undefined && + !resVars[key].format.includes(formatValue) + ) { + assertionRes = false + } }) - //Here we will compare the response values with values we send - const resVals = res[testTable] + // Here we will compare the response values with values we send + const resValues = res[testTable] testTableWithSpecialNumericLowercase[testTable].forEach( (row: { [key: string]: any }, i: number) => Object.keys(row).forEach((col: string) => { if (col === 'specialnumeric') { - if (resVals[i][col.toUpperCase()] !== row[col].toUpperCase()) assertionRes = false + if ( + resValues[i][col.toUpperCase()] !== row[col].toUpperCase() + ) { + assertionRes = false + } } else { - if (resVals[i][col.toUpperCase()] !== row[col]) assertionRes = false + if (resValues[i][col.toUpperCase()] !== row[col]) { + assertionRes = false + } } }) ) From ff5dc5f196a6166af93c7b7f7236920db5795b8f Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 3 Mar 2022 10:59:27 +0300 Subject: [PATCH 4/7] fix(special-missing): fix generateFileUploadForm func --- src/file/generateFileUploadForm.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/file/generateFileUploadForm.ts b/src/file/generateFileUploadForm.ts index dd5661c..7a3f7da 100644 --- a/src/file/generateFileUploadForm.ts +++ b/src/file/generateFileUploadForm.ts @@ -18,7 +18,11 @@ export const generateFileUploadForm = ( if (!Array.isArray(data[tableName])) continue const name = tableName - const csv = convertToCSV(data[tableName]) + const formatsObj = + data[`$${tableName}`] && data[`$${tableName}`].formats + ? data[`$${tableName}`] + : undefined + const csv = convertToCSV(data[tableName], formatsObj) if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( From 88f1c2f843a82f1d9828179795f3e5db4e7386ea Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Thu, 3 Mar 2022 13:00:14 +0100 Subject: [PATCH 5/7] chore: added viya web approach test (special missing) --- sasjs-tests/package-lock.json | 4 +- sasjs-tests/src/testSuites/SpecialCases.ts | 51 +++++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 53400d6..2840748 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -2388,7 +2388,7 @@ "node_modules/@sasjs/adapter": { "version": "5.0.0", "resolved": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-JZsvovkPNb0dlnBRh1uDdvgJg+BE0ebIomWwhYjHj3YApds4MgI++1pa+Ck2tltIUUYASeiPUIPJJX8tF3VCTw==", + "integrity": "sha512-O4MnLA6Tm2xoANU1uBJ5xMzEBeXVGkw38lR6LoiLIUhQOJY5gpwjb39oBPFqPfvCwjG0Jqjx7XcaE+g23pEfNw==", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -20998,7 +20998,7 @@ }, "@sasjs/adapter": { "version": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-JZsvovkPNb0dlnBRh1uDdvgJg+BE0ebIomWwhYjHj3YApds4MgI++1pa+Ck2tltIUUYASeiPUIPJJX8tF3VCTw==", + "integrity": "sha512-O4MnLA6Tm2xoANU1uBJ5xMzEBeXVGkw38lR6LoiLIUhQOJY5gpwjb39oBPFqPfvCwjG0Jqjx7XcaE+g23pEfNw==", "requires": { "@sasjs/utils": "2.36.1", "axios": "0.26.0", diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 2b77581..9e85cf2 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -416,6 +416,55 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ return assertionRes } - } + }, + { + title: 'Special missing values (ONE ROW) useComputeApi undefined', + description: + 'Should support special missing values, when one row is send (On VIYA Web Approach)', + test: () => { + return adapter.request( + 'common/sendObj', + testTableWithSpecialNumericOneRow, + { useComputeApi: undefined } + ) + }, + assertion: (res: any) => { + let assertionRes = true + + // We receive formats in response. We compare it with formats that we included in request to make sure they are equal + const resVars = res[`$${testTable}`].vars + + Object.keys(resVars).forEach((key: any, i: number) => { + let formatValue = + testTableWithSpecialNumeric[`$${testTable}`].formats[ + key.toLowerCase() + ] + // If it is char, we change it to $ to be compatible for comparsion + // If it is number, it will already be compatible to comapre (best.) + formatValue = formatValue?.includes('$') ? '$' : formatValue + + if ( + formatValue !== undefined && + !resVars[key].format.includes(formatValue) + ) { + assertionRes = false + } + }) + + // Here we will compare the response values with values we send + const resValues = res[testTable] + + testTableWithSpecialNumericOneRow[testTable].forEach( + (row: { [key: string]: any }, i: number) => + Object.keys(row).forEach((col: string) => { + if (resValues[i][col.toUpperCase()] !== row[col]) { + assertionRes = false + } + }) + ) + + return assertionRes + } + }, ] }) From 5927910a527dbff4cea86d26a7c5fc81a47264f4 Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 3 Mar 2022 15:07:01 +0300 Subject: [PATCH 6/7] refactor(convert-to-csv): change func arguments --- src/api/viya/uploadTables.ts | 2 +- src/file/generateFileUploadForm.ts | 6 +- src/file/generateTableUploadForm.ts | 9 +- src/job-execution/Sas9JobExecutor.ts | 3 +- src/utils/convertToCsv.spec.ts | 229 ++++++++++++++++----------- src/utils/convertToCsv.ts | 21 ++- src/utils/formatDataForRequest.ts | 7 +- 7 files changed, 165 insertions(+), 112 deletions(-) diff --git a/src/api/viya/uploadTables.ts b/src/api/viya/uploadTables.ts index b9e4402..47f3208 100644 --- a/src/api/viya/uploadTables.ts +++ b/src/api/viya/uploadTables.ts @@ -18,7 +18,7 @@ export async function uploadTables( const uploadedFiles = [] for (const tableName in data) { - const csv = convertToCSV(data[tableName]) + const csv = convertToCSV(data, tableName) if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( 'The max length of a string value in SASjs is 32765 characters.' diff --git a/src/file/generateFileUploadForm.ts b/src/file/generateFileUploadForm.ts index 7a3f7da..04e9c30 100644 --- a/src/file/generateFileUploadForm.ts +++ b/src/file/generateFileUploadForm.ts @@ -18,11 +18,7 @@ export const generateFileUploadForm = ( if (!Array.isArray(data[tableName])) continue const name = tableName - const formatsObj = - data[`$${tableName}`] && data[`$${tableName}`].formats - ? data[`$${tableName}`] - : undefined - const csv = convertToCSV(data[tableName], formatsObj) + const csv = convertToCSV(data, tableName) if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( diff --git a/src/file/generateTableUploadForm.ts b/src/file/generateTableUploadForm.ts index 450333b..f2e8b08 100644 --- a/src/file/generateTableUploadForm.ts +++ b/src/file/generateTableUploadForm.ts @@ -9,18 +9,24 @@ export const generateTableUploadForm = ( const sasjsTables = [] const requestParams: any = {} let tableCounter = 0 + for (const tableName in data) { tableCounter++ + sasjsTables.push(tableName) - const csv = convertToCSV(data[tableName]) + + const csv = convertToCSV(data, tableName) + if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( 'The max length of a string value in SASjs is 32765 characters.' ) } + // if csv has length more then 16k, send in chunks if (csv.length > 16000) { const csvChunks = splitChunks(csv) + // append chunks to form data with same key csvChunks.map((chunk) => { formData.append(`sasjs${tableCounter}data`, chunk) @@ -29,6 +35,7 @@ export const generateTableUploadForm = ( requestParams[`sasjs${tableCounter}data`] = csv } } + requestParams['sasjs_tables'] = sasjsTables.join(' ') return { formData, requestParams } diff --git a/src/job-execution/Sas9JobExecutor.ts b/src/job-execution/Sas9JobExecutor.ts index 82336b4..b1c1fd0 100644 --- a/src/job-execution/Sas9JobExecutor.ts +++ b/src/job-execution/Sas9JobExecutor.ts @@ -125,7 +125,8 @@ const generateFileUploadForm = ( ): NodeFormData => { for (const tableName in data) { const name = tableName - const csv = convertToCSV(data[tableName]) + const csv = convertToCSV(data, tableName) + if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( 'The max length of a string value in SASjs is 32765 characters.' diff --git a/src/utils/convertToCsv.spec.ts b/src/utils/convertToCsv.spec.ts index 9ce9f9d..5d2af1c 100644 --- a/src/utils/convertToCsv.spec.ts +++ b/src/utils/convertToCsv.spec.ts @@ -1,183 +1,222 @@ import { convertToCSV } from './convertToCsv' describe('convertToCsv', () => { + const tableName = 'testTable' + it('should convert single quoted values', () => { - const data = [ - { foo: `'bar'`, bar: 'abc' }, - { foo: 'sadf', bar: 'def' }, - { foo: 'asd', bar: `'qwert'` } - ] + const data = { + [tableName]: [ + { foo: `'bar'`, bar: 'abc' }, + { foo: 'sadf', bar: 'def' }, + { foo: 'asd', bar: `'qwert'` } + ] + } const expectedOutput = `foo:$char5. bar:$char7.\r\n"'bar'",abc\r\nsadf,def\r\nasd,"'qwert'"` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert double quoted values', () => { - const data = [ - { foo: `"bar"`, bar: 'abc' }, - { foo: 'sadf', bar: 'def' }, - { foo: 'asd', bar: `"qwert"` } - ] + const data = { + [tableName]: [ + { foo: `"bar"`, bar: 'abc' }, + { foo: 'sadf', bar: 'def' }, + { foo: 'asd', bar: `"qwert"` } + ] + } const expectedOutput = `foo:$char5. bar:$char7.\r\n"""bar""",abc\r\nsadf,def\r\nasd,"""qwert"""` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `'blah'`, bar: `"blah"` }] + const data = { [tableName]: [{ foo: `'blah'`, bar: `"blah"` }] } const expectedOutput = `foo:$char6. bar:$char6.\r\n"'blah'","""blah"""` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `'blah,"'`, bar: `"blah,blah" "` }] + const data = { [tableName]: [{ foo: `'blah,"'`, bar: `"blah,blah" "` }] } const expectedOutput = `foo:$char8. bar:$char13.\r\n"'blah,""'","""blah,blah"" """` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `',''`, bar: `","` }] + const data = { [tableName]: [{ foo: `',''`, bar: `","` }] } const expectedOutput = `foo:$char4. bar:$char3.\r\n"',''",""","""` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `','`, bar: `,"` }] + const data = { [tableName]: [{ foo: `','`, bar: `,"` }] } const expectedOutput = `foo:$char3. bar:$char2.\r\n"','",","""` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `"`, bar: `'` }] + const data = { [tableName]: [{ foo: `"`, bar: `'` }] } const expectedOutput = `foo:$char1. bar:$char1.\r\n"""","'"` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with mixed quotes', () => { - const data = [{ foo: `,`, bar: `',` }] + const data = { [tableName]: [{ foo: `,`, bar: `',` }] } const expectedOutput = `foo:$char1. bar:$char2.\r\n",","',"` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with number cases 1', () => { - const data = [ - { 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: '' } - ] + const data = { + [tableName]: [ + { 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: '' } + ] + } const expectedOutput = `col1:best. col2:best. col3:$char1. col4:$char1.\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with number cases 2', () => { - const data = [ - { 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' } - ] + const data = { + [tableName]: [ + { 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' } + ] + } const expectedOutput = `col1:best. col2:best. col3:$char1. col4:$char1.\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,1.62,x,x\r\n42,1.62,x,x` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) }) it('should convert values with common special characters', () => { - expect(convertToCSV([{ tab: '\t' }])).toEqual(`tab:$char1.\r\n\"\t\"`) - expect(convertToCSV([{ lf: '\n' }])).toEqual(`lf:$char1.\r\n\"\n\"`) - expect(convertToCSV([{ semicolon: ';semi' }])).toEqual( - `semicolon:$char5.\r\n;semi` + expect(convertToCSV({ [tableName]: [{ tab: '\t' }] }, tableName)).toEqual( + `tab:$char1.\r\n\"\t\"` ) - expect(convertToCSV([{ percent: '%' }])).toEqual(`percent:$char1.\r\n%`) - expect(convertToCSV([{ singleQuote: "'" }])).toEqual( - `singleQuote:$char1.\r\n\"'\"` - ) - expect(convertToCSV([{ doubleQuote: '"' }])).toEqual( - `doubleQuote:$char1.\r\n""""` - ) - expect(convertToCSV([{ crlf: '\r\n' }])).toEqual(`crlf:$char2.\r\n\"\n\"`) - expect(convertToCSV([{ euro: '€euro' }])).toEqual(`euro:$char7.\r\n€euro`) - expect(convertToCSV([{ banghash: '!#banghash' }])).toEqual( - `banghash:$char10.\r\n!#banghash` + expect(convertToCSV({ [tableName]: [{ lf: '\n' }] }, tableName)).toEqual( + `lf:$char1.\r\n\"\n\"` ) + expect( + convertToCSV({ [tableName]: [{ semicolon: ';semi' }] }, tableName) + ).toEqual(`semicolon:$char5.\r\n;semi`) + expect( + convertToCSV({ [tableName]: [{ percent: '%' }] }, tableName) + ).toEqual(`percent:$char1.\r\n%`) + expect( + convertToCSV({ [tableName]: [{ singleQuote: "'" }] }, tableName) + ).toEqual(`singleQuote:$char1.\r\n\"'\"`) + expect( + convertToCSV({ [tableName]: [{ doubleQuote: '"' }] }, tableName) + ).toEqual(`doubleQuote:$char1.\r\n""""`) + expect( + convertToCSV({ [tableName]: [{ crlf: '\r\n' }] }, tableName) + ).toEqual(`crlf:$char2.\r\n\"\n\"`) + expect( + convertToCSV({ [tableName]: [{ euro: '€euro' }] }, tableName) + ).toEqual(`euro:$char7.\r\n€euro`) + expect( + convertToCSV({ [tableName]: [{ banghash: '!#banghash' }] }, tableName) + ).toEqual(`banghash:$char10.\r\n!#banghash`) }) it('should convert values with other special characters', () => { - const data = [ - { - speech0: '"speech', - pct: '%percent', - speech: '"speech', - slash: '\\slash', - slashWithSpecial: '\\\tslash', - macvar: '&sysuserid', - chinese: '传/傳chinese', - sigma: 'Σsigma', - at: '@at', - serbian: 'Српски', - dollar: '$' - } - ] + const data = { + [tableName]: [ + { + speech0: '"speech', + pct: '%percent', + speech: '"speech', + slash: '\\slash', + slashWithSpecial: '\\\tslash', + macvar: '&sysuserid', + chinese: '传/傳chinese', + sigma: 'Σsigma', + at: '@at', + serbian: 'Српски', + dollar: '$' + } + ] + } const expectedOutput = `speech0:$char7. pct:$char8. speech:$char7. slash:$char6. slashWithSpecial:$char7. macvar:$char10. chinese:$char14. sigma:$char7. at:$char3. serbian:$char12. dollar:$char1.\r\n"""speech",%percent,"""speech",\\slash,\"\\\tslash\",&sysuserid,传/傳chinese,Σsigma,@at,Српски,$` - expect(convertToCSV(data)).toEqual(expectedOutput) + expect(convertToCSV(data, tableName)).toEqual(expectedOutput) - expect(convertToCSV([{ speech: 'menext' }])).toEqual( - `speech:$char6.\r\nmenext` - ) - expect(convertToCSV([{ speech: 'me\nnext' }])).toEqual( - `speech:$char7.\r\n\"me\nnext\"` - ) - expect(convertToCSV([{ speech: `me'next` }])).toEqual( - `speech:$char7.\r\n\"me'next\"` - ) - expect(convertToCSV([{ speech: `me"next` }])).toEqual( - `speech:$char7.\r\n\"me""next\"` - ) - expect(convertToCSV([{ speech: `me""next` }])).toEqual( - `speech:$char8.\r\n\"me""""next\"` - ) - expect(convertToCSV([{ slashWithSpecial: '\\\tslash' }])).toEqual( - `slashWithSpecial:$char7.\r\n\"\\\tslash\"` - ) - expect(convertToCSV([{ slashWithSpecial: '\\ \tslash' }])).toEqual( - `slashWithSpecial:$char8.\r\n\"\\ \tslash\"` - ) expect( - convertToCSV([{ slashWithSpecialExtra: '\\\ts\tl\ta\ts\t\th\t' }]) + convertToCSV({ [tableName]: [{ speech: 'menext' }] }, tableName) + ).toEqual(`speech:$char6.\r\nmenext`) + expect( + convertToCSV({ [tableName]: [{ speech: 'me\nnext' }] }, tableName) + ).toEqual(`speech:$char7.\r\n\"me\nnext\"`) + expect( + convertToCSV({ [tableName]: [{ speech: `me'next` }] }, tableName) + ).toEqual(`speech:$char7.\r\n\"me'next\"`) + expect( + convertToCSV({ [tableName]: [{ speech: `me"next` }] }, tableName) + ).toEqual(`speech:$char7.\r\n\"me""next\"`) + expect( + convertToCSV({ [tableName]: [{ speech: `me""next` }] }, tableName) + ).toEqual(`speech:$char8.\r\n\"me""""next\"`) + expect( + convertToCSV( + { [tableName]: [{ slashWithSpecial: '\\\tslash' }] }, + tableName + ) + ).toEqual(`slashWithSpecial:$char7.\r\n\"\\\tslash\"`) + expect( + convertToCSV( + { [tableName]: [{ slashWithSpecial: '\\ \tslash' }] }, + tableName + ) + ).toEqual(`slashWithSpecial:$char8.\r\n\"\\ \tslash\"`) + expect( + convertToCSV( + { [tableName]: [{ slashWithSpecialExtra: '\\\ts\tl\ta\ts\t\th\t' }] }, + tableName + ) ).toEqual(`slashWithSpecialExtra:$char13.\r\n\"\\\ts\tl\ta\ts\t\th\t\"`) }) it('should console log error if data has mixed types', () => { const colName = 'var1' - const data = [{ [colName]: 'string' }, { [colName]: 232 }] + const data = { [tableName]: [{ [colName]: 'string' }, { [colName]: 232 }] } jest.spyOn(console, 'error').mockImplementation(() => {}) - convertToCSV(data) + convertToCSV(data, tableName) expect(console.error).toHaveBeenCalledWith( `Row (2), Column (${colName}) has mixed types: ERROR` ) }) + + it('should throw an error if table was not found in data object', () => { + const data = { [tableName]: [{ var1: 'string' }] } + + expect(() => convertToCSV(data, 'wrongTableName')).toThrow( + new Error('No table provided to be converted to CSV') + ) + }) }) diff --git a/src/utils/convertToCsv.ts b/src/utils/convertToCsv.ts index 73af3ba..9d439a7 100644 --- a/src/utils/convertToCsv.ts +++ b/src/utils/convertToCsv.ts @@ -3,10 +3,15 @@ * @param data - the array of JSON objects to convert. */ export const convertToCSV = ( - data: any[], - sasFormats?: { formats: { [key: string]: string } } + data: { [key: string]: any }, + tableName: string ) => { - let formats = sasFormats?.formats + if (!data[tableName]) { + throw new Error('No table provided to be converted to CSV') + } + + const table = data[tableName] + let formats = data[`$${tableName}`]?.formats let headers: string[] = [] let csvTest let invalidString = false @@ -16,14 +21,14 @@ export const convertToCSV = ( headers = Object.keys(formats).map((key) => `${key}:${formats![key]}`) } - const headerFields = Object.keys(data[0]) + const headerFields = Object.keys(table[0]) headerFields.forEach((field) => { if (!formats || !Object.keys(formats).includes(field)) { let hasNullOrNumber = false let hasSpecialMissingString = false - data.forEach((row: { [key: string]: any }) => { + table.forEach((row: { [key: string]: any }) => { if (row[field] === null || typeof row[field] === 'number') { hasNullOrNumber = true } else if ( @@ -45,7 +50,7 @@ export const convertToCSV = ( let hasMixedTypes: boolean = false let rowNumError: number = -1 - const longestValueForField = data + const longestValueForField = table .map((row: any, index: number) => { if (row[field] || row[field] === '') { if (firstFoundType) { @@ -101,7 +106,7 @@ export const convertToCSV = ( } }) - if (sasFormats) { + if (formats) { headers = headers.sort( (a, b) => headerFields.indexOf(a.replace(/:.*/, '')) - @@ -111,7 +116,7 @@ export const convertToCSV = ( if (invalidString) return 'ERROR: LARGE STRING LENGTH' - csvTest = data.map((row: any) => { + csvTest = table.map((row: any) => { const fields = Object.keys(row).map((fieldName, index) => { let value const currentCell = row[fieldName] diff --git a/src/utils/formatDataForRequest.ts b/src/utils/formatDataForRequest.ts index d223432..bf73457 100644 --- a/src/utils/formatDataForRequest.ts +++ b/src/utils/formatDataForRequest.ts @@ -15,19 +15,24 @@ export const formatDataForRequest = (data: any) => { } tableCounter++ + sasjsTables.push(tableName) - const csv = convertToCSV(data[tableName], data[`$${tableName}`]) + + const csv = convertToCSV(data, tableName) if (csv === 'ERROR: LARGE STRING LENGTH') { throw new Error( 'The max length of a string value in SASjs is 32765 characters.' ) } + // if csv has length more then 16k, send in chunks if (csv.length > 16000) { const csvChunks = splitChunks(csv) + // append chunks to form data with same key result[`sasjs${tableCounter}data0`] = csvChunks.length + csvChunks.forEach((chunk, index) => { result[`sasjs${tableCounter}data${index + 1}`] = chunk }) From f6abb61c69912eaec85041341368a2d858dd03e2 Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 3 Mar 2022 17:31:23 +0300 Subject: [PATCH 7/7] fix(convert-to-csv): return empty string if table is not an array --- sasjs-tests/package-lock.json | 4 ++-- sasjs-tests/src/testSuites/SpecialCases.ts | 2 +- src/utils/convertToCsv.spec.ts | 6 ++++++ src/utils/convertToCsv.ts | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 2840748..1117451 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -2388,7 +2388,7 @@ "node_modules/@sasjs/adapter": { "version": "5.0.0", "resolved": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-O4MnLA6Tm2xoANU1uBJ5xMzEBeXVGkw38lR6LoiLIUhQOJY5gpwjb39oBPFqPfvCwjG0Jqjx7XcaE+g23pEfNw==", + "integrity": "sha512-lbDWueAEnfNlu4OGrc9hBEzT0aoLfAy7eLd2nLHArrF6zukcSGBNhUgOqxIhlz4WeBdf1gt3nk1G7p5X1mrWYQ==", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -20998,7 +20998,7 @@ }, "@sasjs/adapter": { "version": "file:../build/sasjs-adapter-5.0.0.tgz", - "integrity": "sha512-O4MnLA6Tm2xoANU1uBJ5xMzEBeXVGkw38lR6LoiLIUhQOJY5gpwjb39oBPFqPfvCwjG0Jqjx7XcaE+g23pEfNw==", + "integrity": "sha512-lbDWueAEnfNlu4OGrc9hBEzT0aoLfAy7eLd2nLHArrF6zukcSGBNhUgOqxIhlz4WeBdf1gt3nk1G7p5X1mrWYQ==", "requires": { "@sasjs/utils": "2.36.1", "axios": "0.26.0", diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 9e85cf2..23db5c2 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -465,6 +465,6 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({ return assertionRes } - }, + } ] }) diff --git a/src/utils/convertToCsv.spec.ts b/src/utils/convertToCsv.spec.ts index 5d2af1c..7c5f927 100644 --- a/src/utils/convertToCsv.spec.ts +++ b/src/utils/convertToCsv.spec.ts @@ -219,4 +219,10 @@ describe('convertToCsv', () => { new Error('No table provided to be converted to CSV') ) }) + + it('should empty string if table is not an array', () => { + const data = { [tableName]: true } + + expect(convertToCSV(data, tableName)).toEqual('') + }) }) diff --git a/src/utils/convertToCsv.ts b/src/utils/convertToCsv.ts index 9d439a7..bab7788 100644 --- a/src/utils/convertToCsv.ts +++ b/src/utils/convertToCsv.ts @@ -11,6 +11,9 @@ export const convertToCSV = ( } const table = data[tableName] + + if (!Array.isArray(table)) return '' + let formats = data[`$${tableName}`]?.formats let headers: string[] = [] let csvTest