From 906db2dc9d1d2bd22f3a8811d68a4c5dbe47c6b0 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Thu, 4 May 2023 21:55:22 +0530 Subject: [PATCH] test: include extDb tests Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- tests/playwright/package-lock.json | 264 ++++++++++++++++-- tests/playwright/package.json | 1 + tests/playwright/pages/Dashboard/TreeView.ts | 17 ++ .../Dashboard/common/Cell/DateTimeCell.ts | 20 +- tests/playwright/tests/db/timezone.spec.ts | 255 ++++++++++++++++- 5 files changed, 513 insertions(+), 44 deletions(-) diff --git a/tests/playwright/package-lock.json b/tests/playwright/package-lock.json index 18189b8a16..14cc95303a 100644 --- a/tests/playwright/package-lock.json +++ b/tests/playwright/package-lock.json @@ -12,6 +12,7 @@ "body-parser": "^1.20.1", "dayjs": "^1.11.7", "express": "^4.18.2", + "knex": "^2.4.2", "nocodb-sdk": "file:../../packages/nocodb-sdk", "xlsx": "^0.18.5" }, @@ -1097,14 +1098,12 @@ "node_modules/colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" }, "node_modules/commander": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", - "dev": true, "engines": { "node": "^12.20.0 || >=14" } @@ -1408,6 +1407,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1780,6 +1787,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -2167,6 +2182,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -2204,6 +2227,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2482,6 +2510,14 @@ "node": ">= 0.4" } }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2534,7 +2570,6 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -2772,6 +2807,85 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/knex": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz", + "integrity": "sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==", + "dependencies": { + "colorette": "2.0.19", + "commander": "^9.1.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.5.0", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/knex/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2907,8 +3021,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -3718,8 +3831,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { "version": "0.1.7", @@ -3764,8 +3876,7 @@ "node_modules/pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==", - "dev": true + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -4093,6 +4204,17 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4135,7 +4257,6 @@ "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -4609,7 +4730,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4655,6 +4775,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4667,6 +4795,14 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "engines": { + "node": ">=8" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5736,14 +5872,12 @@ "colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" }, "commander": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", - "dev": true + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==" }, "concat-map": { "version": "0.0.1", @@ -5979,6 +6113,11 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6241,6 +6380,11 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -6541,6 +6685,11 @@ "has-symbols": "^1.0.3" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -6563,6 +6712,11 @@ "get-intrinsic": "^1.1.1" } }, + "getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6760,6 +6914,11 @@ "side-channel": "^1.0.4" } }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6794,7 +6953,6 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -6963,6 +7121,47 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "knex": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz", + "integrity": "sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==", + "requires": { + "colorette": "2.0.19", + "commander": "^9.1.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.5.0", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7059,8 +7258,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.merge": { "version": "4.6.2", @@ -7711,8 +7909,7 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "0.1.7", @@ -7743,8 +7940,7 @@ "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==", - "dev": true + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" }, "pg-int8": { "version": "1.0.1", @@ -7983,6 +8179,14 @@ } } }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "requires": { + "resolve": "^1.20.0" + } + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -8010,7 +8214,6 @@ "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, "requires": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -8355,8 +8558,7 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "table": { "version": "6.8.1", @@ -8391,6 +8593,11 @@ } } }, + "tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8403,6 +8610,11 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/tests/playwright/package.json b/tests/playwright/package.json index 16b9708480..59befbff34 100644 --- a/tests/playwright/package.json +++ b/tests/playwright/package.json @@ -46,6 +46,7 @@ "body-parser": "^1.20.1", "dayjs": "^1.11.7", "express": "^4.18.2", + "knex": "^2.4.2", "nocodb-sdk": "file:../../packages/nocodb-sdk", "xlsx": "^0.18.5" } diff --git a/tests/playwright/pages/Dashboard/TreeView.ts b/tests/playwright/pages/Dashboard/TreeView.ts index 804cc1d2fe..29a42fc5e2 100644 --- a/tests/playwright/pages/Dashboard/TreeView.ts +++ b/tests/playwright/pages/Dashboard/TreeView.ts @@ -48,6 +48,23 @@ export class TreeViewPage extends BasePage { await this.get().locator(`.nc-project-tree-tbl-${title}`).focus(); } + async openBase({ title }: { title: string }) { + const nodes = await this.get().locator(`.ant-collapse`); + // loop through nodes.count() to find the node with title + for (let i = 0; i < (await nodes.count()); i++) { + const node = nodes.nth(i); + const nodeTitle = await node.innerText(); + // check if nodeTitle contains title + if (nodeTitle.includes(title)) { + // click on node + await node.click(); + break; + } + } + + await this.rootPage.waitForTimeout(1000); + } + // assumption: first view rendered is always GRID // async openTable({ diff --git a/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts b/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts index dea4bcaa45..2514afcd1d 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts @@ -23,7 +23,7 @@ export class DateTimeCellPageObject extends BasePage { } async save() { - await this.rootPage.locator('button:has-text("Ok")').click(); + await this.rootPage.locator('button:has-text("Ok"):visible').click(); } async selectDate({ @@ -36,15 +36,15 @@ export class DateTimeCellPageObject extends BasePage { const [year, month, day] = date.split('-'); // configure year - await this.rootPage.locator('.ant-picker-year-btn').click(); + await this.rootPage.locator('.ant-picker-year-btn:visible').click(); await this.rootPage.locator(`td[title="${year}"]`).click(); // configure month - await this.rootPage.locator('.ant-picker-month-btn').click(); + await this.rootPage.locator('.ant-picker-month-btn:visible').click(); await this.rootPage.locator(`td[title="${year}-${month}"]`).click(); // configure day - await this.rootPage.locator(`td[title="${year}-${month}-${day}"]`).click(); + await this.rootPage.locator(`td[title="${year}-${month}-${day}"]:visible`).click(); } async selectTime({ @@ -60,14 +60,20 @@ export class DateTimeCellPageObject extends BasePage { second?: number | null; }) { await this.rootPage - .locator(`.ant-picker-time-panel-column:nth-child(1) > .ant-picker-time-panel-cell:nth-child(${hour + 1})`) + .locator( + `.ant-picker-time-panel-column:nth-child(1) > .ant-picker-time-panel-cell:nth-child(${hour + 1}):visible` + ) .click(); await this.rootPage - .locator(`.ant-picker-time-panel-column:nth-child(2) > .ant-picker-time-panel-cell:nth-child(${minute + 1})`) + .locator( + `.ant-picker-time-panel-column:nth-child(2) > .ant-picker-time-panel-cell:nth-child(${minute + 1}):visible` + ) .click(); if (second != null) { await this.rootPage - .locator(`.ant-picker-time-panel-column:nth-child(3) > .ant-picker-time-panel-cell:nth-child(${second + 1})`) + .locator( + `.ant-picker-time-panel-column:nth-child(3) > .ant-picker-time-panel-cell:nth-child(${second + 1}):visible` + ) .click(); } } diff --git a/tests/playwright/tests/db/timezone.spec.ts b/tests/playwright/tests/db/timezone.spec.ts index eae44ccbd4..e64f8fa28b 100644 --- a/tests/playwright/tests/db/timezone.spec.ts +++ b/tests/playwright/tests/db/timezone.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test'; import { DashboardPage } from '../../pages/Dashboard'; import setup from '../../setup'; +import { knex } from 'knex'; import { Api, UITypes } from 'nocodb-sdk'; let api: Api, records: any[]; @@ -25,7 +26,7 @@ const rowAttributes = [ { Id: 3, DateTime: '2020-12-31 20:00:00-04:00' }, ]; -test.describe('Timezone : Europe/Berlin', () => { +test.describe('Timezone : Japan/Tokyo', () => { let dashboard: DashboardPage; let context: any; @@ -57,14 +58,15 @@ test.describe('Timezone : Europe/Berlin', () => { await page.reload(); }); + // DST independent test test.use({ - locale: 'de-DE', // Change to German locale - timezoneId: 'Europe/Berlin', + locale: 'ja-JP', // Change to Japanese locale + timezoneId: 'Asia/Tokyo', // Set timezone to Tokyo timezone }); /* * This test is to verify the display value of DateTime column in the grid - * when the timezone is set to Europe/Berlin + * when the timezone is set to Asia/Tokyo * * The test inserts 3 rows using API * 1. DateTime inserted without timezone @@ -72,24 +74,24 @@ test.describe('Timezone : Europe/Berlin', () => { * 3. DateTime inserted with timezone (UTC-4) * * Expected display values: - * Display value is converted to Europe/Berlin + * Display value is converted to Asia/Tokyo */ test('API insert, verify display value', async () => { await dashboard.treeView.openTable({ title: 'dateTimeTable' }); // DateTime inserted using API without timezone is converted to UTC - // Display value is converted to Europe/Berlin - await dashboard.grid.cell.verifyDateCell({ index: 0, columnHeader: 'DateTime', value: '2021-01-01 01:00' }); + // Display value is converted to Asia/Tokyo + await dashboard.grid.cell.verifyDateCell({ index: 0, columnHeader: 'DateTime', value: '2021-01-01 09:00' }); // DateTime inserted using API with timezone is converted to UTC - // Display value is converted to Europe/Berlin - await dashboard.grid.cell.verifyDateCell({ index: 1, columnHeader: 'DateTime', value: '2021-01-01 01:00' }); - await dashboard.grid.cell.verifyDateCell({ index: 2, columnHeader: 'DateTime', value: '2021-01-01 01:00' }); + // Display value is converted to Asia/Tokyo + await dashboard.grid.cell.verifyDateCell({ index: 1, columnHeader: 'DateTime', value: '2021-01-01 09:00' }); + await dashboard.grid.cell.verifyDateCell({ index: 2, columnHeader: 'DateTime', value: '2021-01-01 09:00' }); }); /* * This test is to verify the API read response of DateTime column - * when the timezone is set to Europe/Berlin + * when the timezone is set to Asia/Tokyo * * The test inserts 3 rows using API * 1. DateTime inserted without timezone @@ -313,3 +315,234 @@ test.describe('Timezone', () => { await dashboard.grid.cell.verifyDateCell({ index: 1, columnHeader: 'DateTime', value: '2021-01-01 08:00' }); }); }); + +async function createTableWithDateTimeColumn(database: string) { + if (database === 'pg') { + const config = { + client: 'pg', + connection: { + host: 'localhost', + port: 5432, + user: 'postgres', + password: 'password', + database: 'postgres', + multipleStatements: true, + }, + searchPath: ['public', 'information_schema'], + pool: { min: 0, max: 5 }, + }; + + const config2 = { + ...config, + connection: { + ...config.connection, + database: 'datetimetable', + }, + }; + const pgknex = knex(config); + await pgknex.raw(`DROP DATABASE IF EXISTS dateTimeTable`); + await pgknex.raw(`CREATE DATABASE dateTimeTable`); + await pgknex.destroy(); + + const pgknex2 = knex(config2); + await pgknex2.raw(` + CREATE TABLE my_table ( + title SERIAL PRIMARY KEY, + datetime_without_tz TIMESTAMP WITHOUT TIME ZONE, + datetime_with_tz TIMESTAMP WITH TIME ZONE + ); + SET timezone = 'Asia/Hong_Kong'; + SELECT pg_sleep(1); + INSERT INTO my_table (datetime_without_tz, datetime_with_tz) + VALUES + ('2023-04-27 10:00:00', '2023-04-27 12:30:00'), + ('2023-04-27 10:00:00+05:30', '2023-04-27 10:00:00+05:30'); + `); + await pgknex2.destroy(); + } else if (database === 'mysql') { + const config = { + client: 'mysql', + connection: { + host: 'localhost', + port: 3306, + user: 'root', + password: 'password', + database: 'sakila', + }, + pool: { min: 0, max: 5 }, + }; + + const config2 = { + ...config, + connection: { + ...config.connection, + database: 'datetimetable', + }, + }; + + const mysqlknex = knex(config); + await mysqlknex.raw(`DROP DATABASE IF EXISTS dateTimeTable`); + await mysqlknex.raw(`CREATE DATABASE dateTimeTable`); + await mysqlknex.destroy(); + + const mysqlknex2 = knex(config2); + await mysqlknex2.raw(` + CREATE TABLE my_table ( + title INT AUTO_INCREMENT PRIMARY KEY, + datetime_without_tz DATETIME, + datetime_with_tz TIMESTAMP + ); + SET time_zone = '+08:00'; + SELECT sleep(1); + INSERT INTO my_table (datetime_without_tz, datetime_with_tz) + VALUES + ('2023-04-27 10:00:00', '2023-04-27 12:30:00'), + ('2023-04-27 10:00:00+05:30', '2023-04-27 10:00:00+05:30'); + `); + await mysqlknex2.destroy(); + } else if (database === 'sqlite') { + const config = { + client: 'sqlite3', + connection: { + filename: './mydb.sqlite3', + }, + useNullAsDefault: true, + pool: { min: 0, max: 5 }, + }; + + // SQLite supports just one type of datetime + // Timezone information, if specified is stored as is in the database + // https://www.sqlite.org/lang_datefunc.html + + const sqliteknex = knex(config); + await sqliteknex.raw(`DROP TABLE IF EXISTS my_table`); + await sqliteknex.raw(` + CREATE TABLE my_table ( + title INTEGER PRIMARY KEY AUTOINCREMENT, + datetime_without_tz DATETIME, + datetime_with_tz DATETIME + ) +`); + const datetimeData = [ + ['2023-04-27 10:00:00', '2023-04-27 10:00:00'], + ['2023-04-27 10:00:00+05:30', '2023-04-27 10:00:00+05:30'], + ]; + for (const data of datetimeData) { + await sqliteknex('my_table').insert({ + datetime_without_tz: data[0], + datetime_with_tz: data[1], + }); + } + await sqliteknex.destroy(); + } +} + +test.describe('External DB - DateTime column', async () => { + let dashboard: DashboardPage; + let context: any; + + test.use({ + locale: 'zh-HK', + timezoneId: 'Asia/Hong_Kong', + }); + + test.beforeEach(async ({ page }) => { + context = await setup({ page, isEmptyProject: true }); + dashboard = new DashboardPage(page, context.project); + + api = new Api({ + baseURL: `http://localhost:8080/`, + headers: { + 'xc-auth': context.token, + }, + }); + + await createTableWithDateTimeColumn(context.dbType); + + await api.base.create(context.project.id, { + alias: 'datetimetable', + type: 'pg', + config: { + client: 'pg', + connection: { + host: 'localhost', + port: '5432', + user: 'postgres', + password: 'password', + database: 'datetimetable', + }, + searchPath: ['public'], + }, + inflection_column: 'camelize', + inflection_table: 'camelize', + }); + + await dashboard.rootPage.reload(); + }); + + test('Verify display value, UI insert, API response', async () => { + await dashboard.treeView.openBase({ title: 'datetimetable' }); + await dashboard.treeView.openTable({ title: 'MyTable' }); + + // display value for datetime column without tz should be same as stored value + // display value for datetime column with tz should be converted to browser timezone (HK in this case) + await dashboard.grid.cell.verifyDateCell({ + index: 0, + columnHeader: 'DatetimeWithoutTz', + value: '2023-04-27 10:00', + }); + await dashboard.grid.cell.verifyDateCell({ + index: 1, + columnHeader: 'DatetimeWithoutTz', + value: '2023-04-27 10:00', + }); + await dashboard.grid.cell.verifyDateCell({ + index: 0, + columnHeader: 'DatetimeWithTz', + value: '2023-04-27 12:30', + }); + await dashboard.grid.cell.verifyDateCell({ + index: 1, + columnHeader: 'DatetimeWithTz', + value: '2023-04-27 12:30', + }); + + // Insert new row + await dashboard.grid.cell.dateTime.setDateTime({ + index: 2, + columnHeader: 'DatetimeWithoutTz', + dateTime: '2023-04-27 10:00:00', + }); + await dashboard.rootPage.waitForTimeout(1000); + await dashboard.grid.cell.dateTime.setDateTime({ + index: 2, + columnHeader: 'DatetimeWithTz', + dateTime: '2023-04-27 12:30:00', + }); + + // verify API response + // Note that, for UI inserted records - second part of datetime may be non-zero (though not shown in UI) + // Hence, we skip seconds from API response + // + const records = await api.dbTableRow.list('noco', context.project.id, 'MyTable', { limit: 10 }); + let dateTimeWithoutTz = records.list.map(record => record.DatetimeWithoutTz); + let dateTimeWithTz = records.list.map(record => record.DatetimeWithTz); + const expectedDateTimeWithoutTz = ['2023-04-27 10:00:00', '2023-04-27 10:00:00', '2023-04-27 10:00:00']; + const expectedDateTimeWithTz = ['2023-04-27T04:30:00.000Z', '2023-04-27T04:30:00.000Z', '2023-04-27T04:30:00.000Z']; + + dateTimeWithoutTz = dateTimeWithoutTz.map(dateTimeStr => { + const [datePart, timePart] = dateTimeStr.split(' '); + const updatedTimePart = timePart.split(':').slice(0, 2).join(':') + ':00'; + return `${datePart} ${updatedTimePart}`; + }); + + dateTimeWithTz = dateTimeWithTz.map(dateTimeStr => { + const dateObj = new Date(dateTimeStr); + dateObj.setSeconds(0); + return dateObj.toISOString(); + }); + + expect(dateTimeWithoutTz).toEqual(expectedDateTimeWithoutTz); + expect(dateTimeWithTz).toEqual(expectedDateTimeWithTz); + }); +});