diff --git a/.env.example b/.env.example index fdf920188..2e6e7e5b9 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,11 @@ PORTAL_RUNNER_PASSWORD= PORTAL_RUNNER_SUBSCRIPTION= PORTAL_RUNNER_RESOURCE_GROUP= PORTAL_RUNNER_DATABASE_ACCOUNT= +PORTAL_RUNNER_DATABASE_ACCOUNT_KEY= PORTAL_RUNNER_CONNECTION_STRING= +NOTEBOOKS_TEST_RUNNER_TENANT_ID= +NOTEBOOKS_TEST_RUNNER_CLIENT_ID= +NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET= CASSANDRA_CONNECTION_STRING= MONGO_CONNECTION_STRING= TABLES_CONNECTION_STRING= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bee4b01cd..d48c70a62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,6 +146,13 @@ jobs: shell: bash env: NODE_TLS_REJECT_UNAUTHORIZED: 0 + PORTAL_RUNNER_SUBSCRIPTION: ${{ secrets.PORTAL_RUNNER_SUBSCRIPTION }} + PORTAL_RUNNER_RESOURCE_GROUP: ${{ secrets.PORTAL_RUNNER_RESOURCE_GROUP }} + PORTAL_RUNNER_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT }} + PORTAL_RUNNER_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY }} + NOTEBOOKS_TEST_RUNNER_TENANT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_TENANT_ID }} + NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }} + NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }} PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }} MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }} CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }} diff --git a/package-lock.json b/package-lock.json index 0823cb93c..4635ddf5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,130 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@azure/abort-controller": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.1.tgz", + "integrity": "sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A==", + "requires": { + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@azure/arm-cosmosdb": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@azure/arm-cosmosdb/-/arm-cosmosdb-9.1.0.tgz", + "integrity": "sha512-ZHQTnBSjJ+TUAlXqfc1M23A0622gSSvYVd5gCqWHwG64e/R4zAySDDXcIi0bGYAUv/0nZzKHYulrgYpU+GnDjw==", + "requires": { + "@azure/ms-rest-azure-js": "^2.0.1", + "@azure/ms-rest-js": "^2.0.4", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@azure/core-auth": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.1.3.tgz", + "integrity": "sha512-A4xigW0YZZpkj1zK7dKuzbBpGwnhEcRk6WWuIshdHC32raR3EQ1j6VA9XZqE+RFsUgH6OAmIK5BWIz+mZjnd6Q==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-tracing": "1.0.0-preview.8", + "@opentelemetry/api": "^0.6.1", + "tslib": "^2.0.0" + }, + "dependencies": { + "@azure/core-tracing": { + "version": "1.0.0-preview.8", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz", + "integrity": "sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q==", + "requires": { + "@opencensus/web-types": "0.0.7", + "@opentelemetry/api": "^0.6.1", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@opentelemetry/api": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.6.1.tgz", + "integrity": "sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q==", + "requires": { + "@opentelemetry/context-base": "^0.6.1" + } + } + } + }, + "@azure/core-http": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.9.tgz", + "integrity": "sha512-wM0HMRNQaE2NtTHb+9FXF7uxUqaAHFTMVu6OzlEll6gUGybcDqM7+9Oklp33BhEfq+ZumpCoqxq3njNbMHuf/w==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.1.3", + "@azure/core-tracing": "1.0.0-preview.9", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^0.10.2", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.1", + "form-data": "^3.0.0", + "node-fetch": "^2.6.0", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^2.0.0", + "tunnel": "^0.0.6", + "uuid": "^8.1.0", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + } + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.9", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz", + "integrity": "sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug==", + "requires": { + "@opencensus/web-types": "0.0.7", + "@opentelemetry/api": "^0.10.2", + "tslib": "^2.0.0" + } + }, "@azure/cosmos": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.9.0.tgz", @@ -46,6 +170,111 @@ } } }, + "@azure/identity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-1.1.0.tgz", + "integrity": "sha512-S4jYqegLWXIwVnkiArFlcTA7KOZmv+LMhQeQJhnmYy/CxrJHyIAEQyJ7qsrSt58bSyDZI2NkmKUBKaYGZU3/5g==", + "requires": { + "@azure/core-http": "^1.1.6", + "@azure/core-tracing": "1.0.0-preview.9", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^0.10.2", + "events": "^3.0.0", + "jws": "^4.0.0", + "keytar": "^5.4.0", + "msal": "^1.0.2", + "qs": "^6.7.0", + "tslib": "^2.0.0", + "uuid": "^8.1.0" + }, + "dependencies": { + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + } + } + }, + "@azure/logger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.0.tgz", + "integrity": "sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA==", + "requires": { + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@azure/ms-rest-azure-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-js/-/ms-rest-azure-js-2.0.1.tgz", + "integrity": "sha512-5e+A710O7gRFISoV4KI/ZyLQbKmjXxQZ1L8Z/sx7jSUQqmswjTnN4yyIZxs5JzfLVkobU0rXxbi5/LVzaI8QXQ==", + "requires": { + "@azure/ms-rest-js": "^2.0.4", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@azure/ms-rest-js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz", + "integrity": "sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg==", + "requires": { + "@types/node-fetch": "^2.3.7", + "@types/tunnel": "0.0.1", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.0", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^3.3.2", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -3449,6 +3678,31 @@ } } }, + "@opencensus/web-types": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@opencensus/web-types/-/web-types-0.0.7.tgz", + "integrity": "sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g==" + }, + "@opentelemetry/api": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.10.2.tgz", + "integrity": "sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA==", + "requires": { + "@opentelemetry/context-base": "^0.10.2" + }, + "dependencies": { + "@opentelemetry/context-base": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.10.2.tgz", + "integrity": "sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw==" + } + } + }, + "@opentelemetry/context-base": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.6.1.tgz", + "integrity": "sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ==" + }, "@peculiar/asn1-schema": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.1.2.tgz", @@ -4152,8 +4406,7 @@ "@types/node": { "version": "12.11.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", - "integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A==", - "dev": true + "integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A==" }, "@types/node-fetch": { "version": "2.5.7", @@ -4369,6 +4622,14 @@ "integrity": "sha512-kAMOjud0Nw3HPY0Cu8cTFk0LVya8skY+ajb2rgrSahPQ6AreN0cpGBNrs8Kjlu9EhFIeh5cp7phovL7v9HrPdQ==", "dev": true }, + "@types/tunnel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "requires": { + "@types/node": "*" + } + }, "@types/uglify-js": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz", @@ -6088,7 +6349,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dev": true, "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -6109,7 +6369,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6446,6 +6705,11 @@ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -8654,6 +8918,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -9425,8 +9697,7 @@ "events": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", - "dev": true + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" }, "eventsource": { "version": "1.0.7", @@ -9516,6 +9787,12 @@ } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -10379,8 +10656,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-exists-sync": { "version": "0.1.0", @@ -10666,6 +10942,12 @@ "assert-plus": "^1.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "optional": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -11808,8 +12090,7 @@ "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "ipaddr.js": { "version": "1.9.1", @@ -14383,6 +14664,35 @@ "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", "dev": true }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "keytar": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-5.6.0.tgz", + "integrity": "sha512-ueulhshHSGoryfRXaIvTj0BV1yB0KddBGhGoqCxSN9LR1Ks1GKuuCdVhF+2/YOs5fMl6MlTI9On1a4DHDXoTow==", + "optional": true, + "requires": { + "nan": "2.14.1", + "prebuild-install": "5.3.3" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -15439,8 +15749,7 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "moment": { "version": "2.29.1", @@ -15506,6 +15815,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "msal": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/msal/-/msal-1.4.3.tgz", + "integrity": "sha512-C90MhgzcBuTSR2BOQ/LQryY1CZVESQLJDdmRDWSsaVde+zwZ2iXD0fWw7zeBd5TzfUCiJEXZVs4lFJ8d/IGbiQ==", + "requires": { + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "multicast-dns": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", @@ -15550,6 +15874,12 @@ "to-regex": "^3.0.1" } }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, "native-promise-only": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", @@ -15644,6 +15974,15 @@ "lower-case": "^1.1.1" } }, + "node-abi": { + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.1.tgz", + "integrity": "sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A==", + "optional": true, + "requires": { + "semver": "^5.4.1" + } + }, "node-abort-controller": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-1.1.0.tgz", @@ -15794,6 +16133,12 @@ "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==", "dev": true }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "optional": true + }, "nopt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", @@ -16873,6 +17218,40 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "prebuild-install": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", + "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -19767,7 +20146,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", - "dev": true, "requires": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -19780,7 +20158,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20272,6 +20649,11 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -20543,8 +20925,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unpipe": { "version": "1.0.0", @@ -21795,6 +22176,12 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "optional": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -21931,6 +22318,22 @@ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "dependencies": { + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } + }, "xmlbuilder": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", diff --git a/package.json b/package.json index cfd5769ef..5b2f9c22a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Cosmos Explorer", "main": "index.js", "dependencies": { + "@azure/arm-cosmosdb": "9.1.0", "@azure/cosmos": "3.9.0", + "@azure/identity": "1.1.0", "@azure/cosmos-language-service": "0.0.5", "@jupyterlab/services": "6.0.0-rc.2", "@jupyterlab/terminal": "3.0.0-rc.2", diff --git a/src/Contracts/ExplorerContracts.ts b/src/Contracts/ExplorerContracts.ts index 088462dbd..0a7807297 100644 --- a/src/Contracts/ExplorerContracts.ts +++ b/src/Contracts/ExplorerContracts.ts @@ -32,7 +32,8 @@ export enum MessageTypes { GetArcadiaToken, CreateWorkspace, CreateSparkPool, - RefreshDatabaseAccount + RefreshDatabaseAccount, + InitTestExplorer } export { Versions, ActionContracts, Diagnostics }; diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index b311cd43b..0d2e1b64a 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -1730,6 +1730,7 @@ export default class Explorer { case MessageTypes.SendNotification: case MessageTypes.ClearNotification: case MessageTypes.LoadingStatus: + case MessageTypes.InitTestExplorer: return true; } } diff --git a/src/Explorer/Panes/UploadFilePane.html b/src/Explorer/Panes/UploadFilePane.html index fc98419bb..ff815d130 100644 --- a/src/Explorer/Panes/UploadFilePane.html +++ b/src/Explorer/Panes/UploadFilePane.html @@ -50,13 +50,24 @@ id="fileImportLinkNotebook" data-bind="event: { click: onImportLinkClick, keypress: onImportLinkKeyPress }" > - upload files + upload files
- +
diff --git a/test/mongo/container.spec.ts b/test/mongo/container.spec.ts index b5e0599de..31ee62762 100644 --- a/test/mongo/container.spec.ts +++ b/test/mongo/container.spec.ts @@ -82,10 +82,6 @@ describe("Collection Add and Delete Mongo spec", () => { ); if (collections.length) { - await frame.waitFor(`div[class="collectionHeader main2 nodeItem "] > div[class="treeNodeHeader "]`, { - visible: true - }); - const textId = await frame.evaluate(element => { return element.attributes["data-test"].textContent; }, collections[0]); diff --git a/test/notebooks/notebookTestUtils.ts b/test/notebooks/notebookTestUtils.ts new file mode 100644 index 000000000..0a81d6e66 --- /dev/null +++ b/test/notebooks/notebookTestUtils.ts @@ -0,0 +1,101 @@ +import { ElementHandle, Frame } from "puppeteer"; +import { TestExplorerParams } from "./testExplorer/TestExplorerParams"; +import * as path from "path"; + +export const NOTEBOOK_OPERATION_DELAY = 5000; +export const RENDER_DELAY = 2500; + +let testExplorerFrame: Frame; +export const getTestExplorerFrame = async (): Promise => { + if (testExplorerFrame) { + return testExplorerFrame; + } + + const notebooksTestRunnerTenantId = process.env.NOTEBOOKS_TEST_RUNNER_TENANT_ID; + const notebooksTestRunnerClientId = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_ID; + const notebooksTestRunnerClientSecret = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET; + const portalRunnerDatabaseAccount = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT; + const portalRunnerDatabaseAccountKey = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY; + const portalRunnerSubscripton = process.env.PORTAL_RUNNER_SUBSCRIPTION; + const portalRunnerResourceGroup = process.env.PORTAL_RUNNER_RESOURCE_GROUP; + + const testExplorerUrl = new URL("testExplorer.html", "https://localhost:1234"); + testExplorerUrl.searchParams.append( + TestExplorerParams.notebooksTestRunnerTenantId, + encodeURI(notebooksTestRunnerTenantId) + ); + testExplorerUrl.searchParams.append( + TestExplorerParams.notebooksTestRunnerClientId, + encodeURI(notebooksTestRunnerClientId) + ); + testExplorerUrl.searchParams.append( + TestExplorerParams.notebooksTestRunnerClientSecret, + encodeURI(notebooksTestRunnerClientSecret) + ); + testExplorerUrl.searchParams.append( + TestExplorerParams.portalRunnerDatabaseAccount, + encodeURI(portalRunnerDatabaseAccount) + ); + testExplorerUrl.searchParams.append( + TestExplorerParams.portalRunnerDatabaseAccountKey, + encodeURI(portalRunnerDatabaseAccountKey) + ); + testExplorerUrl.searchParams.append(TestExplorerParams.portalRunnerSubscripton, encodeURI(portalRunnerSubscripton)); + testExplorerUrl.searchParams.append( + TestExplorerParams.portalRunnerResourceGroup, + encodeURI(portalRunnerResourceGroup) + ); + + await page.goto(testExplorerUrl.toString()); + + const handle = await page.waitForSelector("iframe"); + testExplorerFrame = await handle.contentFrame(); + await testExplorerFrame.waitForSelector(".galleryHeader"); + return testExplorerFrame; +}; + +export const uploadNotebookIfNotExist = async (frame: Frame, notebookName: string): Promise> => { + const notebookNode = await getNotebookNode(frame, notebookName); + if (notebookNode) { + return notebookNode; + } + + const uploadNotebookPath = path.join(__dirname, "testNotebooks", notebookName); + const notebookResourceTree = await frame.waitForSelector(".notebookResourceTree"); + const treeNodeHeadersBeforeUpload = await notebookResourceTree.$$(".treeNodeHeader"); + + const ellipses = await treeNodeHeadersBeforeUpload[2].$("button"); + await ellipses.click(); + + await frame.waitFor(RENDER_DELAY); + + const menuItems = await frame.$$(".ms-ContextualMenu-item"); + await menuItems[4].click(); + + const uploadFileButton = await frame.waitForSelector("#importFileButton"); + uploadFileButton.click(); + + const fileChooser = await page.waitForFileChooser(); + fileChooser.accept([uploadNotebookPath]); + + const submitButton = await frame.waitForSelector("#uploadFileButton"); + await submitButton.click(); + + await frame.waitFor(NOTEBOOK_OPERATION_DELAY); + return await getNotebookNode(frame, notebookName); +}; + +export const getNotebookNode = async (frame: Frame, uploadNotebookName: string): Promise> => { + const notebookResourceTree = await frame.waitForSelector(".notebookResourceTree"); + let currentNotebookNode: ElementHandle; + + const treeNodeHeaders = await notebookResourceTree.$$(".treeNodeHeader"); + for (let i = 1; i < treeNodeHeaders.length; i++) { + currentNotebookNode = treeNodeHeaders[i]; + const nodeLabel = await currentNotebookNode.$eval(".nodeLabel", element => element.textContent); + if (nodeLabel === uploadNotebookName) { + return currentNotebookNode; + } + } + return undefined; +}; diff --git a/test/notebooks/testExplorer/TestExplorer.ts b/test/notebooks/testExplorer/TestExplorer.ts new file mode 100644 index 000000000..ac9cdb071 --- /dev/null +++ b/test/notebooks/testExplorer/TestExplorer.ts @@ -0,0 +1,138 @@ +import { MessageTypes } from "../../../src/Contracts/ExplorerContracts"; +import "../../../less/hostedexplorer.less"; +import { TestExplorerParams } from "./TestExplorerParams"; +import { ClientSecretCredential } from "@azure/identity"; +import { DatabaseAccountsGetResponse } from "@azure/arm-cosmosdb/esm/models"; +import { CosmosDBManagementClient } from "@azure/arm-cosmosdb"; +import * as msRest from "@azure/ms-rest-js"; +import * as ViewModels from "../../../src/Contracts/ViewModels"; + +class CustomSigner implements msRest.ServiceClientCredentials { + private token: string; + constructor(token: string) { + this.token = token; + } + + async signRequest(webResource: msRest.WebResourceLike): Promise { + webResource.headers.set("authorization", `bearer ${this.token}`); + return webResource; + } +} + +const handleMessage = (event: MessageEvent): void => { + if (event.data.type === MessageTypes.InitTestExplorer) { + sendMessageToExplorerFrame(event.data); + } +}; + +const AADLogin = async ( + notebooksTestRunnerApplicationId: string, + notebooksTestRunnerClientId: string, + notebooksTestRunnerClientSecret: string +): Promise => { + const credentials = new ClientSecretCredential( + notebooksTestRunnerApplicationId, + notebooksTestRunnerClientId, + notebooksTestRunnerClientSecret + ); + const token = await credentials.getToken("https://management.core.windows.net/.default"); + return token.token; +}; + +const getDatabaseAccount = async ( + token: string, + notebooksAccountSubscriptonId: string, + notebooksAccountResourceGroup: string, + notebooksAccountName: string +): Promise => { + const client = new CosmosDBManagementClient(new CustomSigner(token), notebooksAccountSubscriptonId); + return client.databaseAccounts.get(notebooksAccountResourceGroup, notebooksAccountName); +}; + +const sendMessageToExplorerFrame = (data: unknown): void => { + const explorerFrame = document.getElementById("explorerMenu") as HTMLIFrameElement; + + explorerFrame && + explorerFrame.contentDocument && + explorerFrame.contentDocument.referrer && + explorerFrame.contentWindow.postMessage( + { + signature: "pcIframe", + data: data + }, + explorerFrame.contentDocument.referrer || window.location.href + ); +}; + +const initTestExplorer = async (): Promise => { + window.addEventListener("message", handleMessage, false); + + const urlSearchParams = new URLSearchParams(window.location.search); + const notebooksTestRunnerTenantId = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.notebooksTestRunnerTenantId) + ); + const notebooksTestRunnerClientId = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.notebooksTestRunnerClientId) + ); + const notebooksTestRunnerClientSecret = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.notebooksTestRunnerClientSecret) + ); + const portalRunnerDatabaseAccount = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.portalRunnerDatabaseAccount) + ); + const portalRunnerDatabaseAccountKey = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.portalRunnerDatabaseAccountKey) + ); + const portalRunnerSubscripton = decodeURIComponent(urlSearchParams.get(TestExplorerParams.portalRunnerSubscripton)); + const portalRunnerResourceGroup = decodeURIComponent( + urlSearchParams.get(TestExplorerParams.portalRunnerResourceGroup) + ); + + const token = await AADLogin( + notebooksTestRunnerTenantId, + notebooksTestRunnerClientId, + notebooksTestRunnerClientSecret + ); + const databaseAccount = await getDatabaseAccount( + token, + portalRunnerSubscripton, + portalRunnerResourceGroup, + portalRunnerDatabaseAccount + ); + + const initTestExplorerContent = { + type: MessageTypes.InitTestExplorer, + inputs: { + databaseAccount: databaseAccount, + subscriptionId: portalRunnerSubscripton, + resourceGroup: portalRunnerResourceGroup, + authorizationToken: `Bearer ${token}`, + features: {}, + hasWriteAccess: true, + csmEndpoint: "https://management.azure.com", + dnsSuffix: "documents.azure.com", + serverId: "prod1", + extensionEndpoint: "/proxy", + subscriptionType: 3, + quotaId: "Internal_2014-09-01", + addCollectionDefaultFlight: "2", + isTryCosmosDBSubscription: false, + masterKey: portalRunnerDatabaseAccountKey, + loadDatabaseAccountTimestamp: 1604663109836, + dataExplorerVersion: "1.0.1", + sharedThroughputMinimum: 400, + sharedThroughputMaximum: 1000000, + sharedThroughputDefault: 400, + defaultCollectionThroughput: { + storage: "100", + throughput: { fixed: 400, unlimited: 400, unlimitedmax: 100000, unlimitedmin: 400, shared: 400 } + }, + // add UI test only when feature is not dependent on flights anymore + flights: [] + } as ViewModels.DataExplorerInputsFrame + }; + + window.postMessage(initTestExplorerContent, window.location.href); +}; + +window.addEventListener("load", initTestExplorer); diff --git a/test/notebooks/testExplorer/TestExplorerParams.ts b/test/notebooks/testExplorer/TestExplorerParams.ts new file mode 100644 index 000000000..1a3e239a7 --- /dev/null +++ b/test/notebooks/testExplorer/TestExplorerParams.ts @@ -0,0 +1,9 @@ +export enum TestExplorerParams { + notebooksTestRunnerTenantId = "notebooksTestRunnerTenantId", + notebooksTestRunnerClientId = "notebooksTestRunnerClientId", + notebooksTestRunnerClientSecret = "notebooksTestRunnerClientSecret", + portalRunnerDatabaseAccount = "portalRunnerDatabaseAccount", + portalRunnerDatabaseAccountKey = "portalRunnerDatabaseAccountKey", + portalRunnerSubscripton = "portalRunnerSubscripton", + portalRunnerResourceGroup = "portalRunnerResourceGroup" +} diff --git a/test/notebooks/testExplorer/testExplorer.html b/test/notebooks/testExplorer/testExplorer.html new file mode 100644 index 000000000..d9a0c8184 --- /dev/null +++ b/test/notebooks/testExplorer/testExplorer.html @@ -0,0 +1,18 @@ + + + + + Azure Cosmos DB + + + + + + + diff --git a/test/notebooks/testNotebooks/GettingStarted.ipynb b/test/notebooks/testNotebooks/GettingStarted.ipynb new file mode 100644 index 000000000..97cd39743 --- /dev/null +++ b/test/notebooks/testNotebooks/GettingStarted.ipynb @@ -0,0 +1,110 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# Getting started with Cosmos notebooks\nIn this notebook, we'll learn how to use Cosmos notebook features. We'll create a database and container, import some sample data in a container in Azure Cosmos DB and run some queries over it." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### Create new database and container\n\nTo connect to the service, you can use our built-in instance of ```cosmos_client```. This is a ready to use instance of [CosmosClient](https://docs.microsoft.com/python/api/azure-cosmos/azure.cosmos.cosmos_client.cosmosclient?view=azure-python) from our Python SDK. It already has the context of this account baked in. We'll use ```cosmos_client``` to create a new database called **RetailDemo** and container called **WebsiteData**.\n\nOur dataset will contain events that occurred on the website - e.g. a user viewing an item, adding it to their cart, or purchasing it. We will partition by CartId, which represents the individual cart of each user. This will give us an even distribution of throughput and storage in our container. Learn more about how to [choose a good partition key.](https://docs.microsoft.com/azure/cosmos-db/partition-data)" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": false + }, + "outputs": [], + "source": "import azure.cosmos\nfrom azure.cosmos.partition_key import PartitionKey\n\ndatabase = cosmos_client.create_database_if_not_exists('RetailDemo')\nprint('Database RetailDemo created')\n\ncontainer = database.create_container_if_not_exists(id='WebsiteData', partition_key=PartitionKey(path='/CartID'))\nprint('Container WebsiteData created')\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "#### Set the default database and container context to the new resources\n\nWe can use the ```%database {database_id}``` and ```%container {container_id}``` syntax." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": false + }, + "outputs": [], + "source": "%database RetailDemo" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": false + }, + "outputs": [], + "source": "%container WebsiteData" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### Load in sample JSON data and insert into the container. \nWe'll use the **%%upload** magic function to insert items into the container" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "inputHidden": false, + "outputHidden": false, + "trusted": false + }, + "outputs": [], + "source": "%%upload --databaseName RetailDemo --containerName WebsiteData --url https://cosmosnotebooksdata.blob.core.windows.net/notebookdata/websiteData-small.json" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "The new database and container should show up under the **Data** section. Use the refresh icon after completing the previous cell. \n\n\"Refresh" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### Run a query using the built-in Azure Cosmos notebook magic\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": false + }, + "outputs": [], + "source": "%%sql\nSELECT c.Action, c.Price as ItemRevenue, c.Country, c.Item FROM c" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "We can get more information about the %%sql command using ```%%sql?```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### Next steps\n\nNow that you've learned how to use basic notebook functionality, follow the **Visualization.ipynb** notebook to further analyze and visualize our data. You can find it under the **Sample Notebooks** section." + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "version": "3.6.8" + }, + "nteract": { + "version": "dataExplorer 1.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/test/notebooks/uploadAndOpenNotebook.spec.ts b/test/notebooks/uploadAndOpenNotebook.spec.ts new file mode 100644 index 000000000..8e539277a --- /dev/null +++ b/test/notebooks/uploadAndOpenNotebook.spec.ts @@ -0,0 +1,29 @@ +import "expect-puppeteer"; +import { getTestExplorerFrame, uploadNotebookIfNotExist } from "./notebookTestUtils"; +import { ElementHandle, Frame } from "puppeteer"; + +jest.setTimeout(300000); + +const notebookName = "GettingStarted.ipynb"; +let frame: Frame; +let uploadedNotebookNode: ElementHandle; + +describe("Notebook UI tests", () => { + it("Upload, Open and Delete Notebook", async () => { + try { + frame = await getTestExplorerFrame(); + uploadedNotebookNode = await uploadNotebookIfNotExist(frame, notebookName); + await uploadedNotebookNode.click(); + await frame.waitForSelector(".tabNavText"); + const tabTitle = await frame.$eval(".tabNavText", element => element.textContent); + expect(tabTitle).toEqual(notebookName); + const closeIcon = await frame.waitForSelector(".close-Icon"); + await closeIcon.click(); + } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const testName = (expect as any).getState().currentTestName; + await page.screenshot({ path: `Test Failed ${testName}.jpg` }); + throw error; + } + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 85238fc47..0c45faa01 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,6 @@ "noEmit": true, "types": ["jest"] }, - "include": ["./src/**/*"], + "include": ["./src/**/*", "./test/notebooks/testExplorer/**/*"], "exclude": ["./src/**/__mocks__/**/*"] } diff --git a/webpack.config.js b/webpack.config.js index 41eb0b920..a90e8896c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -140,6 +140,11 @@ module.exports = function(env = {}, argv = {}) { template: "src/hostedExplorer.html", chunks: ["hostedExplorer"] }), + new HtmlWebpackPlugin({ + filename: "testExplorer.html", + template: "test/notebooks/testExplorer/testExplorer.html", + chunks: ["testExplorer"] + }), new HtmlWebpackPlugin({ filename: "Heatmap.html", template: "src/Controls/Heatmap/Heatmap.html", @@ -178,6 +183,7 @@ module.exports = function(env = {}, argv = {}) { index: "./src/Index.ts", quickstart: "./src/quickstart.ts", hostedExplorer: "./src/HostedExplorer.ts", + testExplorer: "./test/notebooks/testExplorer/TestExplorer.ts", heatmap: "./src/Controls/Heatmap/Heatmap.ts", terminal: "./src/Terminal/index.ts", notebookViewer: "./src/NotebookViewer/NotebookViewer.tsx",