From b4b62f00f56251dc18977dc3d54a170eb6245a45 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Wed, 6 Aug 2025 21:10:33 +0530 Subject: [PATCH 1/8] add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file From 1f8efaaf1ad8864dedf7a625251efa053ce2e0b2 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Fri, 8 Aug 2025 02:19:28 +0530 Subject: [PATCH 2/8] SDK-3773 Added the proto files --- .gitignore | 4 +- buf.gen.yaml | 10 + buf.yaml | 9 + package-lock.json | 541 ++++++++++++++++++ package.json | 36 ++ src/index.ts | 245 ++++++++ .../sdk/v1/sdk-messages-accessibility.proto | 71 +++ .../browserstack/sdk/v1/sdk-messages-ai.proto | 48 ++ .../sdk/v1/sdk-messages-observability.proto | 39 ++ .../sdk/v1/sdk-messages-percy.proto | 10 + .../sdk/v1/sdk-messages-testhub.proto | 10 + .../browserstack/sdk/v1/sdk-messages.proto | 268 +++++++++ src/proto/browserstack/sdk/v1/sdk.proto | 41 ++ tsconfig.json | 24 + 14 files changed, 1355 insertions(+), 1 deletion(-) create mode 100644 buf.gen.yaml create mode 100644 buf.yaml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/index.ts create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages-ai.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages-observability.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages-percy.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk-messages.proto create mode 100644 src/proto/browserstack/sdk/v1/sdk.proto create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index b512c09..780a005 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +node_modules +dist +generated \ No newline at end of file diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..4528589 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,10 @@ +version: v2 +plugins: + - local: protoc-gen-ts_proto + out: src/generated + opt: + - importSuffix=.js + - outputServices=grpc-js + - esModuleInterop=true + - outputPartialMethods=false + - useExactTypes=false diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..1b16d08 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: src/proto/browserstack/sdk/v1 +breaking: + use: + - FILE +lint: + use: + - DEFAULT diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7ad12cd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,541 @@ +{ + "name": "browserstack-proto", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "browserstack-proto", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@bufbuild/protobuf": "^2.5.2", + "@grpc/grpc-js": "1.13.3" + }, + "devDependencies": { + "@bufbuild/buf": "^1.55.1", + "ts-proto": "^2.7.5", + "typescript": "^5.4.5" + } + }, + "node_modules/@bufbuild/buf": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.56.0.tgz", + "integrity": "sha512-1xQWOf3FCDDTi+5B/VScQ73EP6ACwQPCP4ODvCq2L6IVgFtvYX49ur6cQ2qCM8yFitIHESm/Nbff93sh+V/Iog==", + "dev": true, + "hasInstallScript": true, + "bin": { + "buf": "bin/buf", + "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", + "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@bufbuild/buf-darwin-arm64": "1.56.0", + "@bufbuild/buf-darwin-x64": "1.56.0", + "@bufbuild/buf-linux-aarch64": "1.56.0", + "@bufbuild/buf-linux-armv7": "1.56.0", + "@bufbuild/buf-linux-x64": "1.56.0", + "@bufbuild/buf-win32-arm64": "1.56.0", + "@bufbuild/buf-win32-x64": "1.56.0" + } + }, + "node_modules/@bufbuild/buf-darwin-arm64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.56.0.tgz", + "integrity": "sha512-9neaI9gx1sxOGl9xrL7kw6H+0WmVAFlIQTIDc3vt1qRhfgOt/8AWOHSOWppGTRjNiB0qh6Xie1LYHv/jgDVN0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-darwin-x64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.56.0.tgz", + "integrity": "sha512-nRHPMXV8fr/lqU+u/1GGsUg7OvNcxJuCJoJpfRoRg38b+NPzOz2FkQAs5OEJzzprQB5aftn5//cl8YXjgvTuFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-aarch64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.56.0.tgz", + "integrity": "sha512-+td559RuKNwYDnq49NrIDGJ4F73Ra4QzVVbsC+UeveA0HMnIGRzFbchGjHtNJyaZsI57sXJ7dCHH0iFV3jcYwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-armv7": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.56.0.tgz", + "integrity": "sha512-9v3zmos6wRTBc4QeIg4rfDmPzmTgtUTRCbhr87qws/yddIT8cFtHHhy1whnozBNqtmYOdwZNBNx/QXqGGcRuKw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-x64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.56.0.tgz", + "integrity": "sha512-3jZHHBol1fuichNke7LJtHJUdw314XBj6OuJHY6IufsaaVIj1mtM2DPbGiDhYB453J7FiV/buadctKBxAAHclg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-arm64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.56.0.tgz", + "integrity": "sha512-KMGzSf9rIbT01Jb2685JovwRRYEdL7Zbs6ZrjyhIHBgKK6cBwz1AJvEaDrWMEzCdv+opQwjgM6UdtA4e9BWP1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-x64": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.56.0.tgz", + "integrity": "sha512-19LFOCyFFVTaaqNGtYTpiF67fcpneWZFlm8UNU+Xs87Kh+N5i/LjDjNytnpFT6snwU4/S+UUkq7WgS6UPjqXIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.6.3.tgz", + "integrity": "sha512-w/gJKME9mYN7ZoUAmSMAWXk4hkVpxRKvEJCb3dV5g9wwWdxTJJ0ayOJAVcNxtdqaxDyFuC0uz4RSGVacJ030PQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", + "integrity": "sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@types/node": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dprint-node": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", + "integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-poet": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.12.0.tgz", + "integrity": "sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA==", + "dev": true, + "dependencies": { + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.7.5.tgz", + "integrity": "sha512-FoRxSaNW+P3m+GiXIZjUjhaHXT67Ah4zMGKzn4yklbGRQTS+PqpUhKo5AJnwfUDUByjEUG7ch36byFUYWRH9Nw==", + "dev": true, + "dependencies": { + "@bufbuild/protobuf": "^2.0.0", + "case-anything": "^2.1.13", + "ts-poet": "^6.12.0", + "ts-proto-descriptors": "2.0.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.0.0.tgz", + "integrity": "sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==", + "dev": true, + "dependencies": { + "@bufbuild/protobuf": "^2.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..99b5400 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "browserstack-proto", + "version": "1.0.0", + "description": "Protobuf definitions for BrowserStack SDK", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*", + "src/proto/**/*.proto" + ], + "scripts": { + "clean": "rm -rf dist src/generated", + "generate": "buf generate", + "build": "npm run clean && npm run generate && tsc", + "prepare": "npm run build", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "browserstack", + "proto", + "grpc", + "webdriverio" + ], + "author": "BrowserStack", + "license": "MIT", + "dependencies": { + "@bufbuild/protobuf": "^2.5.2", + "@grpc/grpc-js": "1.13.3" + }, + "devDependencies": { + "@bufbuild/buf": "^1.55.1", + "ts-proto": "^2.7.5", + "typescript": "^5.4.5" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..766bfcc --- /dev/null +++ b/src/index.ts @@ -0,0 +1,245 @@ +// Export all proto types +export type { + // Main SDK Messages + DriverInitRequest, + DriverInitResponse, + AutomationFrameworkInitRequest, + AutomationFrameworkInitResponse, + AutomationFrameworkStartRequest, + AutomationFrameworkStartResponse, + AutomationFrameworkStopRequest, + AutomationFrameworkStopResponse, + StartBinSessionRequest, + StartBinSessionResponse, + ConnectBinSessionRequest, + ConnectBinSessionResponse, + StopBinSessionRequest, + StopBinSessionResponse, + TestFrameworkEventRequest, + TestFrameworkEventResponse, + TestSessionEventRequest, + TestSessionEventResponse, + LogCreatedEventRequest, + LogCreatedEventResponse, + FetchDriverExecuteParamsEventRequest, + FetchDriverExecuteParamsEventResponse, + ExecutionContext, + TestSessionEventRequest_AutomationSession as AutomationSession, + LogCreatedEventRequest_LogEntry, + FindNearestHubRequest, + FindNearestHubResponse, + TestOrchestrationRequest, + TestOrchestrationResponse, + TestOrchestration, + RetryTestsOnFailure, + AbortBuildOnFailure, + EnqueueTestEventRequest, + EnqueueTestEventResponse, +} from './generated/sdk-messages.js'; + +// Accessibility types +export type { + Accessibility, + AccessibilityConfigRequest, + AccessibilityConfigResponse, + AccessibilityResultRequest, + AccessibilityResultResponse, +} from './generated/sdk-messages-accessibility.js'; + +// AI types +export type { + AIBrowserExtensionRequest, + AIBrowserExtensionResponse, + AISelfHealGetRequest, + AISelfHealGetResponse, + AISelfHealStepRequest, + AISelfHealStepResponse, +} from './generated/sdk-messages-ai.js'; + +// Observability types +export type { + KeyMessage, + Observability, + ObservabilityConfigRequest, + ObservabilityConfigResponse, +} from './generated/sdk-messages-observability.js'; + +// Percy types +export type { + Percy, +} from './generated/sdk-messages-percy.js'; + +// TestHub types +export type { + TestHub, +} from './generated/sdk-messages-testhub.js'; + +// gRPC Service Client and credentials +export { SDKClient } from './generated/sdk.js'; +export { credentials as grpcCredentials, Channel as grpcChannel, type ChannelCredentials } from '@grpc/grpc-js'; + +// Export message constructors +import * as SdkMessages from './generated/sdk-messages.js'; + +// Create factory objects with create methods for backward compatibility +export const StartBinSessionRequestConstructor = { + create: (init: Partial = {}): SdkMessages.StartBinSessionRequest => { + return { + binSessionId: '', + sdkLanguage: '', + sdkVersion: '', + pathProject: '', + pathConfig: '', + envVars: {}, + cliArgs: [], + frameworks: [], + frameworkVersions: {}, + language: '', + languageVersion: '', + testFramework: '', + wdioConfig: '', + ...init + }; + } +}; + +export const StopBinSessionRequestConstructor = { + create: (init: Partial = {}): SdkMessages.StopBinSessionRequest => { + return { + binSessionId: '', + exitCode: 0, + exitSignal: '', + exitReason: '', + customMetadata: '', + ...init + }; + } +}; + +export const ConnectBinSessionRequestConstructor = { + create: (init: Partial = {}): SdkMessages.ConnectBinSessionRequest => { + return { + binSessionId: '', + platformIndex: 0, + ...init + }; + } +}; + +export const TestFrameworkEventRequestConstructor = { + create: (init: Partial = {}): SdkMessages.TestFrameworkEventRequest => { + return { + binSessionId: '', + platformIndex: 0, + testFrameworkName: '', + testFrameworkVersion: '', + uuid: '', + eventJson: new Uint8Array(), + testFrameworkState: '', + testHookState: '', + startedAt: '', + endedAt: '', + executionContext: undefined, + ...init + }; + } +}; + +export const TestSessionEventRequestConstructor = { + create: (init: Partial = {}): SdkMessages.TestSessionEventRequest => { + return { + binSessionId: '', + platformIndex: 0, + testFrameworkName: '', + testFrameworkVersion: '', + testFrameworkState: '', + testHookState: '', + testUuid: '', + automationSessions: [], + executionContext: undefined, + capabilities: new Uint8Array(), + ...init + }; + } +}; + +export const ExecutionContextConstructor = { + create: (init: Partial = {}): SdkMessages.ExecutionContext => { + return { + threadId: '', + processId: '', + hash: '', + ...init + }; + } +}; + +export const LogCreatedEventRequestConstructor = { + create: (init: Partial = {}): SdkMessages.LogCreatedEventRequest => { + return { + binSessionId: '', + platformIndex: 0, + logs: [], + executionContext: undefined, + ...init + }; + } +}; + +export const LogCreatedEventRequest_LogEntryConstructor = { + create: (init: Partial = {}): SdkMessages.LogCreatedEventRequest_LogEntry => { + return { + testFrameworkName: '', + testFrameworkVersion: '', + testFrameworkState: '', + timestamp: '', + uuid: '', + kind: '', + message: new Uint8Array(), + level: '', + fileName: '', + fileSize: 0, + filePath: '', + ...init + }; + } +}; + +export const AutomationSessionConstructor = { + create: (init: Partial = {}): SdkMessages.TestSessionEventRequest_AutomationSession => { + return { + provider: '', + frameworkName: '', + frameworkVersion: '', + frameworkSessionId: '', + ref: '', + hubUrl: '', + ...init + }; + } +}; + +export const DriverInitRequestConstructor = { + create: (init: Partial = {}): SdkMessages.DriverInitRequest => { + return { + binSessionId: '', + platformIndex: 0, + ref: '', + userInputParams: new Uint8Array(), + ...init + }; + } +}; + +export const FetchDriverExecuteParamsEventRequestConstructor = { + create: (init: Partial = {}): SdkMessages.FetchDriverExecuteParamsEventRequest => { + return { + binSessionId: '', + product: '', + scriptName: '', + ...init + }; + } +}; + + diff --git a/src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto b/src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto new file mode 100644 index 0000000..2ef8e7d --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto @@ -0,0 +1,71 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +import "sdk-messages-observability.proto"; +import "sdk-messages-testhub.proto"; + +message Accessibility { + bool success = 1; + optional AccessibilityOptions options = 2; + optional string error_trace_id = 3; + optional string code = 4; + repeated KeyMessage errors = 5; + optional bool is_app_accessibility = 6; + + message AccessibilityOptions { + optional string status = 1; + repeated AccessibilityCapability capabilities = 2; + repeated AccessibilityScript scripts = 3; + optional AccessibilityCommandsToWrap commands_to_wrap = 4; + + message AccessibilityCommandsToWrap { + repeated string scripts_to_run = 1; + repeated AccessibilityCommand commands = 2; + } + + message AccessibilityCommand { + string name = 1; + string library = 2; + string module = 3; + string class = 4; + string method = 5; + } + + message AccessibilityScript { + string name = 1; + string command = 2; + } + + message AccessibilityCapability { + string name = 1; + string value = 2; + } + } +} + +message AccessibilityConfigRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string framework_name = 3; + string framework_version = 4; + string hub_url = 5; +} + +message AccessibilityConfigResponse { + bool success = 1; + optional TestHub testhub = 2; + optional Accessibility accessibility = 3; +} + +message AccessibilityResultRequest { + string bin_session_id = 1; + string test_run_id = 2; + string session_id = 3; + string result_type = 4; +} + +message AccessibilityResultResponse { + bool success = 1; + bytes result_response = 2; +} diff --git a/src/proto/browserstack/sdk/v1/sdk-messages-ai.proto b/src/proto/browserstack/sdk/v1/sdk-messages-ai.proto new file mode 100644 index 0000000..571a6a2 --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages-ai.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +message AIBrowserExtensionRequest { + string bin_session_id = 1; + string browser_name = 2; + string browser_version = 3; +} + +message AIBrowserExtensionResponse { + bool success = 1; + optional string extension = 2; + optional string error = 3; +} + +message AISelfHealGetRequest { + string bin_session_id = 1; + optional string platform_index = 2; + + string framework_session_id = 3; + string locator_type = 4; +} + +message AISelfHealGetResponse { + bool success = 1; + string framework_session_id = 2; + optional string locator_type = 3; + optional string locator_value = 4; + optional bool ai_healing = 5; + optional string error = 6; +} + +message AISelfHealStepRequest { + string bin_session_id = 1; + optional string platform_index = 2; + + string framework_session_id = 3; + bool is_success = 4; + string locator_type = 5; + string locator_value = 6; + string test_name = 7; +} + +message AISelfHealStepResponse { + string framework_session_id = 1; + string execute_script = 2; +} diff --git a/src/proto/browserstack/sdk/v1/sdk-messages-observability.proto b/src/proto/browserstack/sdk/v1/sdk-messages-observability.proto new file mode 100644 index 0000000..847aa4e --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages-observability.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +import "sdk-messages-testhub.proto"; + +message KeyMessage { + string key = 1; + string message = 2; +} + +message Observability { + bool success = 1; + optional ObservabilityOptions options = 2; + optional string error_trace_id = 3; + optional string code = 4; + repeated KeyMessage errors = 5; + optional bytes options_as_bytes = 6; + + message ObservabilityOptions { + optional string allow_screenshots = 1; + } +} + +message ObservabilityConfigRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string test_framework_name = 3; + string test_framework_version = 4; + optional string hub_url = 5; + optional string capabilities = 6; +} + +message ObservabilityConfigResponse { + bool success = 1; + optional TestHub testhub = 2; + optional Observability observability = 3; + optional string capabilities = 4; +} diff --git a/src/proto/browserstack/sdk/v1/sdk-messages-percy.proto b/src/proto/browserstack/sdk/v1/sdk-messages-percy.proto new file mode 100644 index 0000000..b68b864 --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages-percy.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +message Percy { + bool success = 1; + string percy_capture_mode = 2; + string percy_build_id = 3; + bool is_percy_auto_enabled = 4; +} diff --git a/src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto b/src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto new file mode 100644 index 0000000..9bbc9aa --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +message TestHub { + string jwt = 1; + string build_hashed_id = 2; + bool testhub_events = 3; + bytes errors = 4; +} diff --git a/src/proto/browserstack/sdk/v1/sdk-messages.proto b/src/proto/browserstack/sdk/v1/sdk-messages.proto new file mode 100644 index 0000000..f1ac677 --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk-messages.proto @@ -0,0 +1,268 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +import "sdk-messages-testhub.proto"; +import "sdk-messages-observability.proto"; +import "sdk-messages-accessibility.proto"; +import "sdk-messages-percy.proto"; + +message DriverInitRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string ref = 3; + bytes user_input_params = 4; +} + +message DriverInitResponse { + bool success = 1; + string bin_session_id = 2; + bytes capabilities = 3; + optional string hub_url = 4; +} + +message AutomationFrameworkInitRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string framework_name = 3; + string framework_version = 4; + string ref = 5; + string hub_url = 6; +} + +message AutomationFrameworkInitResponse { + bool success = 1; + optional string hub_url = 2; +} + +message AutomationFrameworkStartRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string framework_name = 3; + string framework_version = 4; + string ref = 5; + string framework_session_id = 6; + string hub_url = 7; +} + +message AutomationFrameworkStartResponse { + bool success = 1; +} + +message AutomationFrameworkStopRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + string framework_name = 3; + string framework_version = 4; + string ref = 5; + string framework_session_id = 6; + string hub_url = 7; +} + +message AutomationFrameworkStopResponse { + bool success = 1; +} + +message FindNearestHubRequest { + string bin_session_id = 1; +} + +message FindNearestHubResponse { + string url = 1; + uint32 latency = 2; +} + +message StartBinSessionRequest { + string bin_session_id = 1; + string sdk_language = 2; + string sdk_version = 3; + string path_project = 4; + string path_config = 5; + map env_vars = 6; + repeated string cli_args = 7; + repeated string frameworks = 8; + map framework_versions = 9; + string language = 10; + optional string language_version = 11; + string test_framework = 12; + optional string wdio_config = 13; +} + +message StartBinSessionResponse { + string bin_session_id = 1; + string config = 2; + optional TestHub testhub = 3; + optional Observability observability = 4; + optional Accessibility accessibility = 5; + optional Percy percy = 6; + optional string session_framework = 7; +} + +message ConnectBinSessionResponse { + string bin_session_id = 1; + string config = 2; + optional TestHub testhub = 3; + optional Observability observability = 4; + optional Accessibility accessibility = 5; + optional Percy percy = 6; + optional string session_framework = 7; + optional TestOrchestration test_orchestration = 8; +} + +message TestOrchestration { + optional bool run_previously_failed_first = 1; + optional RetryTestsOnFailure retry_tests_on_failure = 2; + optional AbortBuildOnFailure abort_build_on_failure = 3; +} + +message RetryTestsOnFailure { + bool enabled = 1; + uint32 max_retries = 2; +} + +message AbortBuildOnFailure { + bool enabled = 1; + uint32 max_failures = 2; +} + +message TestOrchestrationRequest{ + string bin_session_id = 1; + repeated string test_files = 2; + optional string orchestration_strategy = 3; +} + +message TestOrchestrationResponse{ + bool success = 1; + repeated string ordered_test_files = 2; +} + +message StopBinSessionRequest { + string bin_session_id = 1; + optional uint32 exit_code = 2; + optional string exit_signal = 3; + optional string exit_reason = 4; + optional string custom_metadata = 5; +} + +message StopBinSessionResponse { + bool success = 1; + optional string error = 2; + optional string automate_buildlink = 3; + optional string hashed_id = 4; +} + +message ConnectBinSessionRequest { + string bin_session_id = 1; + optional uint32 platform_index = 2; +} + +message TestFrameworkEventRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + + string test_framework_name = 3; + string test_framework_version = 4; + + string uuid = 5; + bytes event_json = 6; + + // event type + string test_framework_state = 7; + string test_hook_state = 8; + + string started_at = 9; + optional string ended_at = 10; + optional ExecutionContext execution_context = 11; +} + +message ExecutionContext { + string thread_id = 1; + optional string process_id = 2; + optional string hash = 3; +} + +message TestFrameworkEventResponse { + bool success = 1; + optional string error = 2; +} + +message EnqueueTestEventRequest { + string bin_session_id = 1; + string event_url = 2; + bytes event_data_json = 3; +} + +message EnqueueTestEventResponse { + bool success = 1; + optional string error = 2; +} + +message LogCreatedEventRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + repeated LogEntry logs = 3; + optional ExecutionContext execution_context = 4; + + message LogEntry { + string test_framework_name = 1; + string test_framework_version = 2; + string test_framework_state = 3; + string timestamp = 4; + string uuid = 5; + string kind = 6; + bytes message = 7; + optional string level = 8; + optional string file_name = 9; + optional int64 file_size = 10; + optional string file_path = 11; + } +} + +message LogCreatedEventResponse { + bool success = 1; + optional string error = 2; +} + +message TestSessionEventRequest { + string bin_session_id = 1; + uint32 platform_index = 2; + + string test_framework_name = 3; + string test_framework_version = 4; + string test_framework_state = 5; + string test_hook_state = 6; + string test_uuid = 7; + + repeated AutomationSession automation_sessions = 8; + optional ExecutionContext execution_context = 9; + + optional bytes capabilities = 10; + + message AutomationSession { + string provider = 1; + optional string product = 2; + string framework_name = 3; + string framework_version = 4; + string framework_session_id = 5; + optional string ref = 6; + optional string hub_url = 7; + } +} + +message TestSessionEventResponse { + bool success = 1; + optional string error = 2; +} + +message FetchDriverExecuteParamsEventRequest { + string bin_session_id = 1; + string product = 2; + optional string script_name = 3; +} + +message FetchDriverExecuteParamsEventResponse { + bool success = 1; + optional string script = 2; + optional bytes accessibility_execute_params = 3; + optional string error = 4; +} diff --git a/src/proto/browserstack/sdk/v1/sdk.proto b/src/proto/browserstack/sdk/v1/sdk.proto new file mode 100644 index 0000000..350a581 --- /dev/null +++ b/src/proto/browserstack/sdk/v1/sdk.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package browserstack.sdk.v1; + +// import "google/protobuf/struct.proto"; +import "sdk-messages-ai.proto"; +import "sdk-messages-accessibility.proto"; +import "sdk-messages-observability.proto"; +import "sdk-messages.proto"; + + +service SDK { + rpc StartBinSession(StartBinSessionRequest) returns (StartBinSessionResponse); + rpc ConnectBinSession(ConnectBinSessionRequest) returns (ConnectBinSessionResponse); + rpc StopBinSession(StopBinSessionRequest) returns (StopBinSessionResponse); + + rpc DriverInit(DriverInitRequest) returns (DriverInitResponse); + rpc AutomationFrameworkInit(AutomationFrameworkInitRequest) returns (AutomationFrameworkInitResponse); + rpc AutomationFrameworkStart(AutomationFrameworkStartRequest) returns (AutomationFrameworkStartResponse); // session-start + rpc AutomationFrameworkStop(AutomationFrameworkStopRequest) returns (AutomationFrameworkStopResponse); + rpc TestOrchestration(TestOrchestrationRequest) returns (TestOrchestrationResponse); + + rpc FindNearestHub(FindNearestHubRequest) returns (FindNearestHubResponse); + + rpc AIBrowserExtension(AIBrowserExtensionRequest) returns (AIBrowserExtensionResponse); + rpc AISelfHealStep(AISelfHealStepRequest) returns (AISelfHealStepResponse); + rpc AISelfHealGetResult(AISelfHealGetRequest) returns (AISelfHealGetResponse); + + rpc AccessibilityConfig(AccessibilityConfigRequest) returns (AccessibilityConfigResponse); + rpc ObservabilityConfig(ObservabilityConfigRequest) returns (ObservabilityConfigResponse); + + rpc AccessibilityResult(AccessibilityResultRequest) returns (AccessibilityResultResponse); + + rpc TestFrameworkEvent(TestFrameworkEventRequest) returns (TestFrameworkEventResponse); + rpc TestSessionEvent(TestSessionEventRequest) returns (TestSessionEventResponse); + rpc EnqueueTestEvent(EnqueueTestEventRequest) returns (EnqueueTestEventResponse); + + rpc LogCreatedEvent(LogCreatedEventRequest) returns (LogCreatedEventResponse); + + rpc FetchDriverExecuteParamsEvent(FetchDriverExecuteParamsEventRequest) returns (FetchDriverExecuteParamsEventResponse); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..02d3282 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} From 08a45daf706d116643aaf83d897b29ac3924e073 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Tue, 12 Aug 2025 15:18:55 +0530 Subject: [PATCH 3/8] Replace old codebase with browserstack-proto --- .gitignore | 3 +- CODEOWNERS | 1 - README.md | 188 ------- browserstack-service.d.ts | 43 -- package.json | 19 +- src/@types/cucumber-framework.d.ts | 15 - src/constants.ts | 10 - src/launcher.ts | 145 ----- src/service.ts | 239 -------- src/types.ts | 40 -- src/util.ts | 101 ---- tests/.eslintrc | 5 - tests/__mocks__/browserstack-local.js | 14 - tests/launcher.test.ts | 152 ----- tests/service.test.ts | 767 -------------------------- tests/util.test.ts | 151 ----- tsconfig.prod.json | 30 - 17 files changed, 4 insertions(+), 1919 deletions(-) delete mode 100644 CODEOWNERS delete mode 100644 README.md delete mode 100644 browserstack-service.d.ts delete mode 100644 src/@types/cucumber-framework.d.ts delete mode 100644 src/constants.ts delete mode 100644 src/launcher.ts delete mode 100644 src/service.ts delete mode 100644 src/types.ts delete mode 100644 src/util.ts delete mode 100644 tests/.eslintrc delete mode 100644 tests/__mocks__/browserstack-local.js delete mode 100644 tests/launcher.test.ts delete mode 100644 tests/service.test.ts delete mode 100644 tests/util.test.ts delete mode 100644 tsconfig.prod.json diff --git a/.gitignore b/.gitignore index fe5aab6..780a005 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules dist -generated -build/ +generated \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 6487822..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @browserstack/default_owner diff --git a/README.md b/README.md deleted file mode 100644 index db569b7..0000000 --- a/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# Deprecation Note - -> The changes done as a part of this fork have now been merged into [@wdio/browserstack-service](https://www.npmjs.com/package/@wdio/browserstack-service), so we will be deprecating this package. Please install the service using: - -```sh -npm install @wdio/browserstack-service --save-dev -``` - ---- - -# WebdriverIO Browserstack Service - -> The official BrowserStack WebdriverIO service that integrates your WebdriverIO test suite with BrowserStack. - -## Installation - -You can simply do it by: - -```sh -npm install @browserstack/wdio-browserstack-service --save-dev -``` - -Instructions on how to install `WebdriverIO` can be found [here.](https://webdriver.io/docs/gettingstarted) - -## Configuration - -WebdriverIO has Browserstack support out of the box. You should simply set `user` and `key` in your `wdio.conf.js` file. This service plugin provides supports for [Browserstack Tunnel](https://www.browserstack.com/automate/node#setting-local-tunnel). Set `browserstackLocal: true` also to activate this feature. -Reporting of session status on BrowserStack will respect `strict` setting of Cucumber options. - -```js -// wdio.conf.js -export.config = { - // ... - user: process.env.BROWSERSTACK_USERNAME, - key: process.env.BROWSERSTACK_ACCESS_KEY, - services: [ - ['@browserstack/wdio-browserstack-service', { - browserstackLocal: true - }] - ], - // ... -}; -``` - -## Options - -In order to authorize to the BrowserStack service your config needs to contain a [`user`](https://webdriver.io/docs/options#user) and [`key`](https://webdriver.io/docs/options#key) option. - -### browserstackLocal - -Set this to true to enable routing connections from Browserstack cloud through your computer. - -Type: `Boolean`
-Default: `false` - -### preferScenarioName - -Cucumber only. Set this to true to enable updating the session name to the Scenario name if only a single Scenario was ran. Useful when running in parallel with [wdio-cucumber-parallel-execution](https://github.com/SimitTomar/wdio-cucumber-parallel-execution). - -Type: `Boolean`
-Default: `false` - -### opts - -Specified optional will be passed down to BrowserstackLocal. - -Type: `Object`
-Default: `{}` - -List of available local testing modifiers to be passed as opts: - -#### Local Identifier - -If doing simultaneous multiple local testing connections, set this uniquely for different processes - - -```js -opts = { localIdentifier: 'randomstring' }; -``` - -#### Verbose Logging - -To enable verbose logging - - -```js -opts = { verbose: 'true' }; -``` - -Note - Possible values for 'verbose' modifier are '1', '2', '3' and 'true' - -#### Force Local - -To route all traffic via local(your) machine - - -```js -opts = { forceLocal: 'true' }; -``` - -#### Folder Testing - -To test local folder rather internal server, provide path to folder as value of this option - - -```js -opts = { f: '/my/awesome/folder' }; -``` - -#### Force Start - -To kill other running Browserstack Local instances - - -```js -opts = { force: 'true' }; -``` - -#### Only Automate - -To disable local testing for Live and Screenshots, and enable only Automate - - -```js -opts = { onlyAutomate: 'true' }; -``` - -#### Proxy - -To use a proxy for local testing - - -- proxyHost: Hostname/IP of proxy, remaining proxy options are ignored if this option is absent -- proxyPort: Port for the proxy, defaults to 3128 when -proxyHost is used -- proxyUser: Username for connecting to proxy (Basic Auth Only) -- proxyPass: Password for USERNAME, will be ignored if USERNAME is empty or not specified - -```js -opts = { - proxyHost: '127.0.0.1', - proxyPort: '8000', - proxyUser: 'user', - proxyPass: 'password', -}; -``` - -#### Local Proxy - -To use local proxy in local testing - - -- localProxyHost: Hostname/IP of proxy, remaining proxy options are ignored if this option is absent -- localProxyPort: Port for the proxy, defaults to 8081 when -localProxyHost is used -- localProxyUser: Username for connecting to proxy (Basic Auth Only) -- localProxyPass: Password for USERNAME, will be ignored if USERNAME is empty or not specified - -```js -opts = { - localProxyHost: '127.0.0.1', - localProxyPort: '8000', - localProxyUser: 'user', - localProxyPass: 'password', -}; -``` - -#### PAC (Proxy Auto-Configuration) - -To use PAC (Proxy Auto-Configuration) in local testing - - -- pac-file: PAC (Proxy Auto-Configuration) file’s absolute path - -```js -opts = { 'pac-file': '' }; -``` - -#### Binary Path - -By default, BrowserStack local wrappers try downloading and executing the latest version of BrowserStack binary in ~/.browserstack or the present working directory or the tmp folder by order. But you can override these by passing the -binarypath argument. -Path to specify local Binary path - - -```js -opts = { binarypath: '/path/to/binary' }; -``` - -#### Logfile - -To save the logs to the file while running with the '-v' argument, you can specify the path of the file. By default the logs are saved in the local.log file in the present woring directory. -To specify the path to file where the logs will be saved - - -```js -opts = { verbose: 'true', logFile: './local.log' }; -``` - ---- - -For more information on WebdriverIO see the [homepage](https://webdriver.io).
diff --git a/browserstack-service.d.ts b/browserstack-service.d.ts deleted file mode 100644 index 9c488d9..0000000 --- a/browserstack-service.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -declare module WebdriverIO { - interface ServiceOption extends BrowserstackConfig {} - - interface Suite { - title: string; - fullName: string; - file: string; - } - - interface Test extends Suite { - parent: string; - passed: boolean; - } - - interface TestResult { - exception: string; - status: string; - } -} - -interface BrowserstackConfig { - /** - * Set this to true to enable routing connections from Browserstack cloud through your computer. - * You will also need to set `browserstack.local` to true in browser capabilities. - */ - browserstackLocal?: boolean; - /** - * Cucumber only. Set this to true to enable updating the session name to the Scenario name if only - * a single Scenario was ran. Useful when running in parallel - * with [wdio-cucumber-parallel-execution](https://github.com/SimitTomar/wdio-cucumber-parallel-execution). - */ - preferScenarioName?: boolean; - /** - * Set this to true to kill the browserstack process on complete, without waiting for the - * browserstack stop callback to be called. This is experimental and should not be used by all. - */ - forcedStop?: boolean; - /** - * Specified optional will be passed down to BrowserstackLocal. See this list for details: - * https://stackoverflow.com/questions/39040108/import-class-in-definition-file-d-ts - */ - opts?: Partial -} diff --git a/package.json b/package.json index 4701803..99b5400 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,7 @@ { - "name": "@browserstack/wdio-browserstack-service", - "version": "2.0.0", - "description": "WebdriverIO service for better Browserstack integration", - "author": "Browserstack", - "homepage": "https://github.com/browserstack/wdio-browserstack-service", + "name": "browserstack-proto", + "version": "1.0.0", + "description": "Protobuf definitions for BrowserStack SDK", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -11,14 +9,6 @@ "dist/**/*", "src/proto/**/*.proto" ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git://github.com/browserstack/wdio-browserstack-service.git" - }, - "types": "./build/index.d.ts", "scripts": { "clean": "rm -rf dist src/generated", "generate": "buf generate", @@ -32,9 +22,6 @@ "grpc", "webdriverio" ], - "bugs": { - "url": "https://github.com/browserstack/wdio-browserstack-service/issues" - }, "author": "BrowserStack", "license": "MIT", "dependencies": { diff --git a/src/@types/cucumber-framework.d.ts b/src/@types/cucumber-framework.d.ts deleted file mode 100644 index 1bb2b3a..0000000 --- a/src/@types/cucumber-framework.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module WebdriverIO { - interface Config extends CucumberOptsConfig {} -} - -interface CucumberOptsConfig { - cucumberOpts?: CucumberOpts -} - -interface CucumberOpts { - /** - * Fail if there are any undefined or pending steps - * @default false - */ - strict?: boolean -} diff --git a/src/constants.ts b/src/constants.ts deleted file mode 100644 index a63f58f..0000000 --- a/src/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const BROWSER_DESCRIPTION = [ - 'device', - 'os', - 'osVersion', - 'os_version', - 'browserName', - 'browser', - 'browserVersion', - 'browser_version' -] as const diff --git a/src/launcher.ts b/src/launcher.ts deleted file mode 100644 index c6db9b4..0000000 --- a/src/launcher.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { promisify } from 'util' -import { performance, PerformanceObserver } from 'perf_hooks' - -import * as BrowserstackLocalLauncher from 'browserstack-local' -import logger from '@wdio/logger' -import type { Capabilities, Services, Options } from '@wdio/types' - -import { BrowserstackConfig } from './types' -import { getWebdriverIOVersion, getBrowserstackWdioServiceVersion } from './util' - -const log = logger('@browserstack/wdio-browserstack-service') - -type BrowserstackLocal = BrowserstackLocalLauncher.Local & { - pid?: number; - stop(callback: (err?: any) => void): void; -} - -export default class BrowserstackLauncherService implements Services.ServiceInstance { - browserstackLocal?: BrowserstackLocal - - constructor ( - private _options: BrowserstackConfig | any, - capabilities: Capabilities.RemoteCapability, - private _config: Options.Testrunner | any - ) { - this._config || (this._config = _options) - } - - onPrepare (config?: Options.Testrunner, capabilities?: Capabilities.RemoteCapabilities) { - if (Array.isArray(capabilities)) { - capabilities.forEach((capability: Capabilities.DesiredCapabilities | any) => { - const wdioServiceVersion: string = getBrowserstackWdioServiceVersion(); - const webdriverIOVersion: any = getWebdriverIOVersion(); - if (capability['bstack:options']) { - // if bstack:options present add wdioService inside it - capability['bstack:options'].wdioService = wdioServiceVersion; - } else if (webdriverIOVersion >= 7) { - // in case of webdriver version 7 we need to add wdioService inside bstack:options, - // so need to add bstack:options key first - capability['bstack:options'] = {}; - capability['bstack:options'].wdioService = wdioServiceVersion; - } else { - // on webdriver 6 and below can directly add at root level - capability['browserstack.wdioService'] = wdioServiceVersion; - } - }) - } - if (!this._options.browserstackLocal) { - return log.info('browserstackLocal is not enabled - skipping...') - } - - const opts = { - key: this._config.key, - forcelocal: this._config.services[0][1] ? this._config.services[0][1].forcelocal : false, - ...this._options.opts - } - - this.browserstackLocal = new BrowserstackLocalLauncher.Local() - - if (Array.isArray(capabilities)) { - capabilities.forEach((capability: Capabilities.DesiredCapabilities) => { - if (capability['bstack:options']) { - capability['bstack:options'].local = true - } else { - capability['browserstack.local'] = true - } - }) - } else if (typeof capabilities === 'object') { - Object.entries(capabilities as Capabilities.MultiRemoteCapabilities).forEach(([, caps]) => { - if ((caps.capabilities as Capabilities.Capabilities)['bstack:options']) { - (caps.capabilities as Capabilities.Capabilities)['bstack:options']!.local = true - } else { - (caps.capabilities as Capabilities.Capabilities)['browserstack.local'] = true - } - }) - } else { - throw TypeError('Capabilities should be an object or Array!') - } - - /** - * measure TestingBot tunnel boot time - */ - const obs = new PerformanceObserver((list) => { - const entry = list.getEntries()[0] - log.info(`Browserstack Local successfully started after ${entry.duration}ms`) - }) - - obs.observe({ entryTypes: ['measure'] }) - - let timer: NodeJS.Timeout - performance.mark('tbTunnelStart') - return Promise.race([ - promisify(this.browserstackLocal.start.bind(this.browserstackLocal))(opts), - new Promise((resolve, reject) => { - /* istanbul ignore next */ - timer = setTimeout(function () { - reject('Browserstack Local failed to start within 60 seconds!') - }, 60000) - })] - ).then(function (result) { - clearTimeout(timer) - performance.mark('tbTunnelEnd') - performance.measure('bootTime', 'tbTunnelStart', 'tbTunnelEnd') - return Promise.resolve(result) - }, function (err) { - clearTimeout(timer) - return Promise.reject(err) - }) - } - - onComplete () { - if (!this.browserstackLocal || !this.browserstackLocal.isRunning()) { - return - } - - if (this._options.forcedStop) { - return process.kill(this.browserstackLocal.pid as number) - } - - let timer: NodeJS.Timeout - return Promise.race([ - new Promise((resolve, reject) => { - this.browserstackLocal?.stop((err: Error) => { - if (err) { - return reject(err) - } - resolve() - }) - }), - new Promise((resolve, reject) => { - /* istanbul ignore next */ - timer = setTimeout( - () => reject(new Error('Browserstack Local failed to stop within 60 seconds!')), - 60000 - ) - })] - ).then(function (result) { - clearTimeout(timer) - return Promise.resolve(result) - }, function (err) { - clearTimeout(timer) - return Promise.reject(err) - }) - } -} diff --git a/src/service.ts b/src/service.ts deleted file mode 100644 index 2c3bd64..0000000 --- a/src/service.ts +++ /dev/null @@ -1,239 +0,0 @@ -import logger from '@wdio/logger' -import got from 'got' -import type { Services, Capabilities, Options, Frameworks } from '@wdio/types' -import type { Browser, MultiRemoteBrowser } from 'webdriverio' - -import { getBrowserDescription, getBrowserCapabilities, isBrowserstackCapability, getParentSuiteName } from './util' -import { BrowserstackConfig, MultiRemoteAction, SessionResponse } from './types' - -const log = logger('@browserstack/wdio-browserstack-service') - -export default class BrowserstackService implements Services.ServiceInstance { - private _sessionBaseUrl = 'https://api.browserstack.com/automate/sessions' - private _failReasons: string[] = [] - private _scenariosThatRan: string[] = [] - private _failureStatuses: string[] = ['failed', 'ambiguous', 'undefined', 'unknown'] - private _browser?: Browser<'async'> | MultiRemoteBrowser<'async'> - private _fullTitle?: string - - constructor ( - private _options: BrowserstackConfig | any, - private _caps: Capabilities.RemoteCapability, - private _config: Options.Testrunner | any - ) { - this._config || (this._config = _options); - // Cucumber specific - const strict = Boolean(this._config.cucumberOpts && this._config.cucumberOpts.strict) - // See https://github.com/cucumber/cucumber-js/blob/master/src/runtime/index.ts#L136 - if (strict) { - this._failureStatuses.push('pending') - } - } - - _updateCaps (fn: (caps: Capabilities.Capabilities | Capabilities.DesiredCapabilities) => void) { - const multiRemoteCap = this._caps as Capabilities.MultiRemoteCapabilities - - if (multiRemoteCap.capabilities) { - return Object.entries(multiRemoteCap).forEach(([, caps]) => fn(caps.capabilities as Capabilities.Capabilities)) - } - - return fn(this._caps as Capabilities.Capabilities) - } - - /** - * if no user and key is specified even though a browserstack service was - * provided set user and key with values so that the session request - * will fail - */ - beforeSession (config: Options.Testrunner) { - if (!config.user) { - config.user = 'NotSetUser' - } - - if (!config.key) { - config.key = 'NotSetKey' - } - this._config.user = config.user - this._config.key = config.key - } - - before(caps: Capabilities.RemoteCapability, specs: string[], browser: Browser<'async'> | MultiRemoteBrowser<'async'>) { - this._browser = browser ? browser : (global as any).browser; - - // Ensure capabilities are not null in case of multiremote - - if (this._isAppAutomate()) { - this._sessionBaseUrl = 'https://api-cloud.browserstack.com/app-automate/sessions' - } - - this._scenariosThatRan = [] - - return this._printSessionURL() - } - - beforeSuite (suite: Frameworks.Suite) { - this._fullTitle = suite.title - } - - beforeFeature(uri: unknown, feature: { name: string }) { - this._fullTitle = feature.name - return this._updateJob({ name: this._fullTitle }) - } - - afterTest(test: Frameworks.Test, context: never, results: Frameworks.TestResult) { - const { error, passed } = results - - // Jasmine - if (test.fullName) { - const testSuiteName = test.fullName.slice(0, test.fullName.indexOf(test.description || '') - 1) - if (this._fullTitle === 'Jasmine__TopLevel__Suite') { - this._fullTitle = testSuiteName - } else if (this._fullTitle) { - this._fullTitle = getParentSuiteName(this._fullTitle, testSuiteName) - } - } else { - // Mocha - this._fullTitle = `${test.parent} - ${test.title}` - } - - if (!passed) { - this._failReasons.push((error && error.message) || 'Unknown Error') - } - } - - after (result: number) { - // For Cucumber: Checks scenarios that ran (i.e. not skipped) on the session - // Only 1 Scenario ran and option enabled => Redefine session name to Scenario's name - if (this._options.preferScenarioName && this._scenariosThatRan.length === 1){ - this._fullTitle = this._scenariosThatRan.pop() - } - - const hasReasons = Boolean(this._failReasons.filter(Boolean).length) - - return this._updateJob({ - status: result === 0 ? 'passed' : 'failed', - name: this._fullTitle, - reason: hasReasons ? this._failReasons.join('\n') : undefined - }) - } - - /** - * For CucumberJS - */ - afterScenario (world: Frameworks.World) { - const status = world.result?.status.toLowerCase() - if (status === 'skipped') { - this._scenariosThatRan.push(world.pickle.name || 'unknown pickle name') - } - - if (status && this._failureStatuses.includes(status)) { - const exception = ( - (world.result && world.result.message) || - (status === 'pending' - ? `Some steps/hooks are pending for scenario "${world.pickle.name}"` - : 'Unknown Error' - ) - ) - - this._failReasons.push(exception) - } - } - - async onReload(oldSessionId: string, newSessionId: string) { - if (!this._browser) { - return Promise.resolve() - } - - const hasReasons = Boolean(this._failReasons.filter(Boolean).length) - - let status = hasReasons ? 'failed' : 'passed' - if (!this._browser.isMultiremote) { - log.info(`Update (reloaded) job with sessionId ${oldSessionId}, ${status}`) - } else { - const browserName = (this._browser as MultiRemoteBrowser<'async'>).instances.filter( - (browserName) => this._browser && (this._browser as MultiRemoteBrowser<'async'>)[browserName].sessionId === newSessionId)[0] - log.info(`Update (reloaded) multiremote job for browser "${browserName}" and sessionId ${oldSessionId}, ${status}`) - } - - await this._update(oldSessionId, { - name: this._fullTitle, - status, - reason: hasReasons ? this._failReasons.join('\n') : undefined - }) - this._scenariosThatRan = [] - delete this._fullTitle - this._failReasons = [] - await this._printSessionURL() - } - - _isAppAutomate(): boolean { - const browserDesiredCapabilities = (this._browser?.capabilities ?? {}) as Capabilities.DesiredCapabilities - const desiredCapabilities = (this._caps ?? {}) as Capabilities.DesiredCapabilities - - return !!browserDesiredCapabilities['appium:app'] || !!desiredCapabilities['appium:app'] || !!browserDesiredCapabilities.app || !!desiredCapabilities.app - } - - _updateJob (requestBody: any) { - return this._multiRemoteAction((sessionId: string, browserName: string) => { - log.info(browserName - ? `Update multiremote job for browser "${browserName}" and sessionId ${sessionId}` - : `Update job with sessionId ${sessionId}` - ) - return this._update(sessionId, requestBody) - }) - } - - _multiRemoteAction (action: MultiRemoteAction) { - const { _browser } = this - if (!_browser) { - return Promise.resolve() - } - - if (!_browser.isMultiremote) { - return action(_browser.sessionId) - } - - return Promise.all(_browser.instances - .filter(browserName => { - const cap = getBrowserCapabilities(_browser, (this._caps as Capabilities.MultiRemoteCapabilities), browserName) - return isBrowserstackCapability(cap) - }) - .map((browserName: string) => ( - action(_browser[browserName].sessionId, browserName) - )) - ) - } - - _update(sessionId: string, requestBody: any) { - const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json` - log.debug(`Updating Browserstack session at ${sessionUrl} with request body: `, requestBody) - return got.put(sessionUrl, { - json: requestBody, - username: this._config.user, - password: this._config.key - }) - } - - async _printSessionURL() { - if (!this._browser) { - return Promise.resolve() - } - await this._multiRemoteAction(async (sessionId, browserName) => { - const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json` - log.debug(`Requesting Browserstack session URL at ${sessionUrl}`) - const response = await got(sessionUrl, { - username: this._config.user, - password: this._config.key, - responseType: 'json' - }) - - if (!this._browser) { - return - } - - const capabilities = getBrowserCapabilities(this._browser, this._caps, browserName) - const browserString = getBrowserDescription(capabilities) - log.info(`${browserString} session: ${response.body.automation_session.browser_url}`) - }) - } -} diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 136a17d..0000000 --- a/src/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -export interface SessionResponse { - // eslint-disable-next-line camelcase - automation_session: { - // eslint-disable-next-line camelcase - browser_url: string - } -} - -export type MultiRemoteAction = (sessionId: string, browserName?: string) => Promise; - -export interface BrowserstackConfig { - /** - * Set this to true to enable routing connections from Browserstack cloud through your computer. - * You will also need to set `browserstack.local` to true in browser capabilities. - */ - browserstackLocal?: boolean; - /** - * Cucumber only. Set this to true to enable updating the session name to the Scenario name if only - * a single Scenario was ran. Useful when running in parallel - * with [wdio-cucumber-parallel-execution](https://github.com/SimitTomar/wdio-cucumber-parallel-execution). - */ - preferScenarioName?: boolean; - /** - * Set this to true to kill the browserstack process on complete, without waiting for the - * browserstack stop callback to be called. This is experimental and should not be used by all. - */ - forcedStop?: boolean; - /** - * Specified optional will be passed down to BrowserstackLocal. For more details check out the - * [`browserstack-local`](https://www.npmjs.com/package/browserstack-local#arguments) docs. - * - * @example - * ```js - * { - * localIdentifier: 'some-identifier' - * } - * ``` - */ - opts?: Partial -} diff --git a/src/util.ts b/src/util.ts deleted file mode 100644 index 343300c..0000000 --- a/src/util.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { Browser, MultiRemoteBrowser } from 'webdriverio' -import type { Capabilities } from '@wdio/types' - -import { BROWSER_DESCRIPTION } from './constants' - -// @ts-ignore -import { version } from '../package.json' - -/** - * get browser description for Browserstack service - * @param cap browser capablities - */ -export function getBrowserDescription(cap: Capabilities.DesiredCapabilities) { - cap = cap || {} - if (cap['bstack:options']) { - cap = { ...cap, ...cap['bstack:options'] } as Capabilities.DesiredCapabilities - } - - /** - * These keys describe the browser the test was run on - */ - return BROWSER_DESCRIPTION - .map((k: keyof Capabilities.DesiredCapabilities) => cap[k]) - .filter(Boolean) - .join(' ') -} - -/** - * get correct browser capabilities object in both multiremote and normal setups - * @param browser browser object - * @param caps browser capbilities object. In case of multiremote, the object itself should have a property named 'capabilities' - * @param browserName browser name in case of multiremote - */ -export function getBrowserCapabilities(browser: Browser<'async'> | MultiRemoteBrowser<'async'>, caps?: Capabilities.RemoteCapability, browserName?: string) { - if (!browser.isMultiremote) { - return { ...browser.capabilities, ...caps } - } - - const multiCaps = caps as Capabilities.MultiRemoteCapabilities - const globalCap = browserName && browser[browserName] ? browser[browserName].capabilities : {} - const cap = browserName && multiCaps[browserName] ? multiCaps[browserName].capabilities : {} - return { ...globalCap, ...cap } as Capabilities.Capabilities -} - -/** - * check for browserstack W3C capabilities. Does not support legacy capabilities - * @param cap browser capabilities - */ -export function isBrowserstackCapability(cap?: Capabilities.Capabilities) { - return Boolean(cap && cap['bstack:options']) -} - -export function getParentSuiteName(fullTitle: string, testSuiteTitle: string): string { - const fullTitleWords = fullTitle.split(' ') - const testSuiteTitleWords = testSuiteTitle.split(' ') - const shortestLength = Math.min(fullTitleWords.length, testSuiteTitleWords.length) - let c = 0 - let parentSuiteName = '' - while (c < shortestLength && fullTitleWords[c] === testSuiteTitleWords[c]) { - parentSuiteName += fullTitleWords[c++] + ' ' - } - return parentSuiteName.trim() -} - -// returns the webdriverIO version being used -export function getWebdriverIOVersion(): any { - let webdriverIOVersion: any = undefined; - // process.env.npm_package_json returns the path of the package.json file - const packageFile: any = process.env.npm_package_json; - if (packageFile !== undefined) { - // try to get the webdriverIO version from the dependencies - const { devDependencies, dependencies } = require(packageFile); - if (devDependencies !== undefined) { - webdriverIOVersion = devDependencies['webdriverio'] - } - if (webdriverIOVersion !== undefined && dependencies !== undefined) { - webdriverIOVersion = dependencies['webdriverio'] - } - } else { - // for node version > 12 process.env.npm_package_json is undefined - // we directly have access to the dependencies, using that here - webdriverIOVersion = process.env.npm_package_dependencies_webdriverio; - if (webdriverIOVersion === undefined) { - webdriverIOVersion = process.env.npm_package_devDependencies_webdriverio; - } - } - if (webdriverIOVersion !== undefined) { - // calculate the major version of the webdriverio package - webdriverIOVersion = webdriverIOVersion.split('.')[0]; - if (webdriverIOVersion[0] === '^') { - webdriverIOVersion = webdriverIOVersion.substring(1); - } - webdriverIOVersion = parseInt(webdriverIOVersion); - } - return webdriverIOVersion; -} - -// import the version from package.json file and return it -export function getBrowserstackWdioServiceVersion(): string { - return version; -} diff --git a/tests/.eslintrc b/tests/.eslintrc deleted file mode 100644 index 55f121d..0000000 --- a/tests/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "jest": true - } -} diff --git a/tests/__mocks__/browserstack-local.js b/tests/__mocks__/browserstack-local.js deleted file mode 100644 index 8834f99..0000000 --- a/tests/__mocks__/browserstack-local.js +++ /dev/null @@ -1,14 +0,0 @@ -const Browserstack = jest.genMockFromModule('browserstack-local') - -export const mockIsRunning = jest.fn().mockImplementation(() => true) -export const mockStart = jest.fn().mockImplementation((options, cb) => cb(null, null)) -export const mockStop = jest.fn().mockImplementation((cb) => cb(null)) - -export const mockLocal = jest.fn().mockImplementation( function () { - this.isRunning = mockIsRunning - this.start = mockStart - this.stop = mockStop -}) - -Browserstack.Local = mockLocal -module.exports = Browserstack diff --git a/tests/launcher.test.ts b/tests/launcher.test.ts deleted file mode 100644 index 9713484..0000000 --- a/tests/launcher.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import Browserstack from 'browserstack-local' -import logger from '@wdio/logger' - -import BrowserstackLauncher from '../src/launcher' -import { BrowserstackConfig } from '../src/types' - -const expect = global.expect as unknown as jest.Expect - -const log = logger('test') -const error = new Error('I\'m an error!') -const sleep = (ms: number = 100) => new Promise((resolve) => setTimeout(resolve, ms)) - -beforeEach(() => { - jest.clearAllMocks() -}) - -describe('onPrepare', () => { - const options: BrowserstackConfig = { browserstackLocal: true } - const caps: any = [{}] - const config = { - user: 'foobaruser', - key: '12345', - capabilities: [] - } - const logInfoSpy = jest.spyOn(log, 'info').mockImplementation((string) => string) - - it('should not call local if browserstackLocal is undefined', () => { - const service = new BrowserstackLauncher({}, caps, { - user: 'foobaruser', - key: '12345', - capabilities: [] - }) - service.onPrepare() - - expect(logInfoSpy).toHaveBeenCalledWith('browserstackLocal is not enabled - skipping...') - expect(service.browserstackLocal).toBeUndefined() - }) - - it('should not call local if browserstackLocal is false', () => { - const service = new BrowserstackLauncher({ - browserstackLocal: false - }, caps, { - user: 'foobaruser', - key: '12345', - capabilities: [] - }) - service.onPrepare() - - expect(logInfoSpy).toHaveBeenCalledWith('browserstackLocal is not enabled - skipping...') - expect(service.browserstackLocal).toBeUndefined() - }) - - it('should initialize the opts object, and spawn a new Local instance', async () => { - const service = new BrowserstackLauncher(options, caps, config) - await service.onPrepare(config, caps) - expect(service.browserstackLocal).toBeDefined() - }) - - it('should add the "browserstack.local" property to a multiremote capability', async () => { - const service = new BrowserstackLauncher(options, caps, config) - const capabilities = { chromeBrowser: { capabilities: {} } } - - await service.onPrepare(config, capabilities) - expect(capabilities.chromeBrowser.capabilities).toEqual({ 'bstack:options': { local: true } }) - }) - - it('should add the "browserstack.local" property to an array of capabilities', async () => { - const service = new BrowserstackLauncher(options, caps, config) - const capabilities = [{}, {}, {}] - - await service.onPrepare(config, capabilities) - expect(capabilities).toEqual([ - { 'bstack:options': { local: true } }, - { 'bstack:options': { local: true } }, - { 'bstack:options': { local: true } } - ]) - }) - - it('should throw an error if "capabilities" is not an object/array', () => { - const service = new BrowserstackLauncher(options, caps, config) - const capabilities = 1 - - expect(() => service.onPrepare(config, capabilities as any)) - .toThrow(TypeError('Capabilities should be an object or Array!')) - }) - - it('should reject if local.start throws an error', () => { - const service = new BrowserstackLauncher(options, caps, config) - const BrowserstackLocalStartSpy = jest.spyOn(new Browserstack.Local(), 'start') - BrowserstackLocalStartSpy.mockImplementationOnce((options, cb) => cb(error)) - - return expect(service.onPrepare(config, caps)).rejects.toThrow(error) - .then(() => expect(service.browserstackLocal?.start).toHaveBeenCalled()) - }) - - it('should successfully resolve if local.start is successful', async () => { - const logInfoMock = jest.spyOn(log, 'info') - const service = new BrowserstackLauncher(options, caps, config) - - await service.onPrepare(config, caps) - expect(service.browserstackLocal?.start).toHaveBeenCalled() - await sleep(100) - expect(logInfoMock.mock.calls[0][0]) - .toContain('Browserstack Local successfully started after') - }) - - it('should correctly set up this-binding for local.start', async () => { - const service = new BrowserstackLauncher(options, caps, config) - const BrowserstackLocalStartSpy = jest.spyOn(new Browserstack.Local(), 'start') - await service.onPrepare(config, caps) - expect(BrowserstackLocalStartSpy).toHaveBeenCalled() - jest.clearAllMocks() - }) -}) - -describe('onComplete', () => { - it('should do nothing if browserstack local is turned on, but not running', () => { - const service = new BrowserstackLauncher({}, [{}] as any, {} as any) - service.browserstackLocal = new Browserstack.Local() - const BrowserstackLocalIsRunningSpy = jest.spyOn(service.browserstackLocal, 'isRunning') - BrowserstackLocalIsRunningSpy.mockImplementationOnce(() => false) - service.onComplete() - expect(service.browserstackLocal.stop).not.toHaveBeenCalled() - }) - - it('should kill the process if forcedStop is true', () => { - const service = new BrowserstackLauncher({ forcedStop: true }, [{}] as any, {} as any) - service.browserstackLocal = new Browserstack.Local() - service.browserstackLocal.pid = 102 - - const killSpy = jest.spyOn(process, 'kill').mockImplementationOnce((pid) => pid as any) - expect(service.onComplete()).toEqual(102) - expect(killSpy).toHaveBeenCalled() - expect(service.browserstackLocal.stop).not.toHaveBeenCalled() - }) - - it('should reject with an error, if local.stop throws an error', () => { - const service = new BrowserstackLauncher({}, [{ browserName: '' }] as any, {} as any) - service.browserstackLocal = new Browserstack.Local() - const BrowserstackLocalStopSpy = jest.spyOn(service.browserstackLocal, 'stop') - BrowserstackLocalStopSpy.mockImplementationOnce((cb) => cb(error)) - return expect(service.onComplete()).rejects.toThrow(error) - .then(() => expect(service.browserstackLocal?.stop).toHaveBeenCalled()) - }) - - it('should properly resolve if everything works', () => { - const service = new BrowserstackLauncher({}, [{}] as any, {} as any) - service.browserstackLocal = new Browserstack.Local() - return expect(service.onComplete()).resolves.toBe(undefined) - .then(() => expect(service.browserstackLocal?.stop).toHaveBeenCalled()) - }) -}) diff --git a/tests/service.test.ts b/tests/service.test.ts deleted file mode 100644 index bae1b7b..0000000 --- a/tests/service.test.ts +++ /dev/null @@ -1,767 +0,0 @@ -import gotMock from 'got' -import logger from '@wdio/logger' -import type { Browser } from 'webdriverio' - -import BrowserstackService from '../src/service' - -interface GotMock extends jest.Mock { - put: jest.Mock -} - -const got = gotMock as unknown as GotMock -const expect = global.expect as unknown as jest.Expect - -const log = logger('test') -let service: BrowserstackService -let browser: Browser - -beforeEach(() => { - (log.info as jest.Mock).mockClear() - got.mockClear() - got.put.mockClear() - got.mockReturnValue(Promise.resolve({ - body: { - automation_session: { - browser_url: 'https://www.browserstack.com/automate/builds/1/sessions/2' - } - } - })) - got.put.mockReturnValue(Promise.resolve({})) - - browser = { - sessionId: 'session123', - config: {}, - capabilities: { - device: '', - os: 'OS X', - os_version: 'Sierra', - browserName: 'chrome' - }, - instances: ['browserA', 'browserB'], - isMultiremote: false, - browserA: { - sessionId: 'session456', - capabilities: { 'bstack:options': { - device: '', - os: 'Windows', - osVersion: 10, - browserName: 'chrome' - } } - }, - browserB: {} - } as any as Browser - service = new BrowserstackService({}, [] as any, { user: 'foo', key: 'bar' } as any) -}) - -it('should initialize correctly', () => { - service = new BrowserstackService({}, [] as any, {} as any) - expect(service['_failReasons']).toEqual([]) -}) - -describe('onReload()', () => { - it('should update and get session', async () => { - const updateSpy = jest.spyOn(service, '_update') - service['_browser'] = browser - await service.onReload('1', '2') - expect(updateSpy).toHaveBeenCalled() - expect(got.put).toHaveBeenCalled() - expect(got).toHaveBeenCalled() - }) - - it('should update and get multiremote session', async () => { - // @ts-expect-error - browser.isMultiremote = true - service['_browser'] = browser - const updateSpy = jest.spyOn(service, '_update') - await service.onReload('1', '2') - expect(updateSpy).toHaveBeenCalled() - expect(got.put).toHaveBeenCalled() - expect(got).toHaveBeenCalled() - }) - - it('should reset failures', async () => { - const updateSpy = jest.spyOn(service, '_update') - service['_browser'] = browser - - service['_failReasons'] = ['Custom Error: Button should be enabled', 'Expected something'] - await service.onReload('1', '2') - expect(updateSpy).toHaveBeenCalledWith('1', { - status: 'failed', - reason: 'Custom Error: Button should be enabled' + '\n' + 'Expected something' - }) - expect(service['_failReasons']).toEqual([]) - }) -}) - -describe('beforeSession', () => { - it('should set some default to make missing user and key parameter apparent', () => { - service.beforeSession({} as any) - expect(service['_config']).toEqual({ user: 'NotSetUser', key: 'NotSetKey' }) - }) - - it('should set username default to make missing user parameter apparent', () => { - service.beforeSession({ user: 'foo' } as any) - expect(service['_config']).toEqual({ user: 'foo', key: 'NotSetKey' }) - }) - - it('should set key default to make missing key parameter apparent', () => { - service.beforeSession({ key: 'bar' } as any) - expect(service['_config']).toEqual({ user: 'NotSetUser', key: 'bar' }) - }) -}) - -describe('_printSessionURL', () => { - it('should get and log session details', async () => { - browser.isMultiremote = false - service['_browser'] = browser - const logInfoSpy = jest.spyOn(log, 'info').mockImplementation((string) => string) - await service._printSessionURL() - expect(got).toHaveBeenCalledWith( - 'https://api.browserstack.com/automate/sessions/session123.json', - { username: 'foo', password: 'bar', responseType: 'json' }) - expect(logInfoSpy).toHaveBeenCalled() - expect(logInfoSpy).toHaveBeenCalledWith( - 'OS X Sierra chrome session: https://www.browserstack.com/automate/builds/1/sessions/2' - ) - }) - - it('should get and log multi remote session details', async () => { - // @ts-expect-error - browser.isMultiremote = true - service['_browser'] = browser - const logInfoSpy = jest.spyOn(log, 'info').mockImplementation((string) => string) - await service._printSessionURL() - expect(got).toHaveBeenCalledWith( - 'https://api.browserstack.com/automate/sessions/session456.json', - { username: 'foo', password: 'bar', responseType: 'json' }) - expect(logInfoSpy).toHaveBeenCalled() - expect(logInfoSpy).toHaveBeenCalledWith( - 'Windows 10 chrome session: https://www.browserstack.com/automate/builds/1/sessions/2' - ) - }) -}) - -describe('_printSessionURL Appium', () => { - beforeEach(() => { - got.mockReturnValue(Promise.resolve({ - body: { - automation_session: { - name: 'Smoke Test', - duration: 65, - os: 'ios', - os_version: '12.1', - browser_version: 'app', - browser: null, - device: 'iPhone XS', - status: 'failed', - reason: 'CLIENT_STOPPED_SESSION', - browser_url: 'https://app-automate.browserstack.com/builds/1/sessions/2' - } - } - })) - - browser.capabilities = { - device: 'iPhone XS', - os: 'iOS', - os_version: '12.1', - browserName: '', - } - }) - - it('should get and log session details', async () => { - service['_browser'] = browser - await service._printSessionURL() - expect(log.info).toHaveBeenCalled() - expect(log.info).toHaveBeenCalledWith( - 'iPhone XS iOS 12.1 session: https://app-automate.browserstack.com/builds/1/sessions/2' - ) - }) -}) - -describe('before', () => { - it('should set auth to default values if not provided', async () => { - let service = new BrowserstackService({}, [{}] as any, { capabilities: {} }) - - await service.beforeSession({} as any as any) - await service.before(service['_config'], [], browser as Browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_config'].user).toEqual('NotSetUser') - expect(service['_config'].key).toEqual('NotSetKey') - - service = new BrowserstackService({}, [{}] as any, { capabilities: {} }) - service.beforeSession({ user: 'blah' } as any as any) - await service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - - expect(service['_config'].user).toEqual('blah') - expect(service['_config'].key).toEqual('NotSetKey') - service = new BrowserstackService({}, [{}] as any, { capabilities: {} }) - service.beforeSession({ key: 'blah' } as any as any) - await service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_config'].user).toEqual('NotSetUser') - expect(service['_config'].key).toEqual('blah') - }) - - it('should initialize correctly', () => { - const service = new BrowserstackService({}, [{}] as any, { - user: 'foo', - key: 'bar', - capabilities: {} - }) - service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_sessionBaseUrl']).toEqual('https://api.browserstack.com/automate/sessions') - }) - - it('should initialize correctly for multiremote', () => { - const service = new BrowserstackService( - {}, - [{}] as any, - { - user: 'foo', - key: 'bar', - capabilities: [{}] - } - ) - service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_sessionBaseUrl']).toEqual('https://api.browserstack.com/automate/sessions') - }) - - it('should initialize correctly for appium', () => { - const service = new BrowserstackService( - {}, - [{ app: 'test-app' }] as any, - { - user: 'foo', - key: 'bar', - capabilities: { - app: 'test-app' - } as any - } - ) - browser.capabilities = { - app: 'test-app', - device: 'iPhone XS', - os: 'iOS', - os_version: '12.1', - browserName: '', - } - service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_sessionBaseUrl']).toEqual('https://api-cloud.browserstack.com/app-automate/sessions') - }) - - it('should initialize correctly for appium without global browser capabilities', () => { - const service = new BrowserstackService({}, { - app: 'bs://BrowserStackMobileAppId' - }, { - user: 'foo', - key: 'bar', - capabilities: { - app: 'test-app' as any - } - }) - service.before(service['_config'], [], browser) - - expect(service['_failReasons']).toEqual([]) - expect(service['_sessionBaseUrl']).toEqual('https://api-cloud.browserstack.com/app-automate/sessions') - }) - - it('should initialize correctly for appium if using valid W3C Webdriver capabilities', () => { - const service = new BrowserstackService({}, { - app: 'bs://BrowserStackMobileAppId' - }, { - user: 'foo', - key: 'bar', - capabilities: { - ['appium:app']: 'test-app' - } - }, browser) - service.before(service._config, [], browser) - - expect(service._failReasons).toEqual([]) - expect(service._sessionBaseUrl).toEqual('https://api-cloud.browserstack.com/app-automate/sessions') - }) - - it('should log the url', async () => { - const service = new BrowserstackService({}, [{}] as any, { capabilities: {} }) - - await service.before(service['_config'], [], browser) - expect(log.info).toHaveBeenCalled() - expect(log.info).toHaveBeenCalledWith( - 'OS X Sierra chrome session: https://www.browserstack.com/automate/builds/1/sessions/2') - }) -}) - -describe('afterTest', () => { - it('should increment failure reasons on fails', () => { - service.before(service['_config'], [], browser) - service['_fullTitle'] = '' - service.beforeSuite({ title: 'foo' } as any) - service.afterTest( - { title: 'foo', parent: 'bar' } as any, - undefined as never, - { error: { message: 'cool reason' }, result: 1, duration: 5, passed: false } as any) - expect(service['_failReasons']).toContain('cool reason') - - service.afterTest( - { title: 'foo2', parent: 'bar2' } as any, - undefined as never, - { error: { message: 'not so cool reason' }, result: 1, duration: 7, passed: false } as any) - - expect(service['_failReasons']).toHaveLength(2) - expect(service['_failReasons']).toContain('cool reason') - expect(service['_failReasons']).toContain('not so cool reason') - - service.afterTest( - { title: 'foo3', parent: 'bar3' } as any, - undefined as never, - { error: undefined, result: 1, duration: 7, passed: false } as any) - - expect(service['_fullTitle']).toBe('bar3 - foo3') - expect(service['_failReasons']).toHaveLength(3) - expect(service['_failReasons']).toContain('cool reason') - expect(service['_failReasons']).toContain('not so cool reason') - expect(service['_failReasons']).toContain('Unknown Error') - }) - - it('should not increment failure reasons on passes', () => { - service.before(service['_config'], [], browser) - service.beforeSuite({ title: 'foo' } as any) - service.afterTest( - { title: 'foo', parent: 'bar' } as any, - undefined as never, - { error: { message: 'cool reason' }, result: 1, duration: 5, passed: true } as any) - expect(service['_failReasons']).toEqual([]) - - service.afterTest( - { title: 'foo2', parent: 'bar2' } as any, - undefined as never, - { error: { message: 'not so cool reason' }, result: 1, duration: 5, passed: true } as any) - - expect(service['_fullTitle']).toBe('bar2 - foo2') - expect(service['_failReasons']).toEqual([]) - }) - - it('should set title for Mocha tests', () => { - service.before(service['_config'], [], browser) - service.beforeSuite({ title: 'foo' } as any) - service.afterTest({ title: 'bar', parent: 'foo' } as any, undefined as never, {} as any) - expect(service['_fullTitle']).toBe('foo - bar') - }) - - describe('Jasmine only', () => { - it('should set suite name of first test as title', () => { - service.before(service['_config'], [], browser) - service.beforeSuite({ title: 'Jasmine__TopLevel__Suite' } as any) - service.afterTest({ fullName: 'foo bar baz', description: 'baz' } as any, undefined as never, {} as any) - expect(service['_fullTitle']).toBe('foo bar') - }) - - it('should set parent suite name as title', () => { - service.before(service['_config'], [], browser) - service.beforeSuite({ title: 'Jasmine__TopLevel__Suite' } as any) - service.afterTest({ fullName: 'foo bar baz', description: 'baz' } as any, undefined as never, {} as any) - service.afterTest({ fullName: 'foo xyz', description: 'xyz' } as any, undefined as never, {} as any) - expect(service['_fullTitle']).toBe('foo') - }) - }) -}) - -describe('afterScenario', () => { - it('should increment failure reasons on non-passing statuses (strict mode off)', () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - - expect(service['_failReasons']).toEqual([]) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual([]) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'FAILED', message: 'I am Error, most likely' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'FAILED', message: 'I too am Error' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely', 'I too am Error']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'UNDEFINED', message: 'Step XYZ is undefined' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely', 'I too am Error', 'Step XYZ is undefined']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'AMBIGUOUS', message: 'Step XYZ2 is ambiguous' } }) - expect(service['_failReasons']).toEqual( - ['I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous']) - - service.afterScenario({ pickle: { name: 'Can do something' }, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'PENDING' } }) - expect(service['_failReasons']).toEqual( - ['I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual([ - 'I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous']) - }) - - it('should increment failure reasons on non-passing statuses (strict mode on)', () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: true }, capabilities: {} }) - - expect(service['_failReasons']).toEqual([]) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual([]) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, message: 'I am Error, most likely', status: 'FAILED' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'FAILED', message: 'I too am Error' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely', 'I too am Error']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'UNDEFINED', message: 'Step XYZ is undefined' } }) - expect(service['_failReasons']).toEqual(['I am Error, most likely', 'I too am Error', 'Step XYZ is undefined']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'AMBIGUOUS', message: 'Step XYZ2 is ambiguous' } }) - expect(service['_failReasons']).toEqual( - ['I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous']) - - service.afterScenario({ pickle: { name: 'Can do something' }, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'PENDING' } }) - expect(service['_failReasons']).toEqual( - ['I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous', - 'Some steps/hooks are pending for scenario "Can do something"']) - - service.afterScenario({ pickle: {}, result: { duration: { seconds: 0, nanos: 1000000 }, willBeRetried: false, status: 'SKIPPED' } }) - expect(service['_failReasons']).toEqual([ - 'I am Error, most likely', - 'I too am Error', - 'Step XYZ is undefined', - 'Step XYZ2 is ambiguous', - 'Some steps/hooks are pending for scenario "Can do something"']) - }) -}) - -describe('after', () => { - it('should call _update when session has no errors (exit code 0)', async () => { - const updateSpy = jest.spyOn(service, '_update') - await service.before(service['_config'], [], browser) - - service['_failReasons'] = [] - service['_fullTitle'] = 'foo - bar' - - await service.after(0) - - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, - { - status: 'passed', - name: 'foo - bar', - reason: undefined - }) - expect(got.put).toHaveBeenCalledWith( - 'https://api.browserstack.com/automate/sessions/session123.json', - { json: { - status: 'passed', - name: 'foo - bar', - reason: undefined - }, username: 'foo', password: 'bar' }) - }) - - it('should call _update when session has errors (exit code 1)', async () => { - const updateSpy = jest.spyOn(service, '_update') - await service.before(service['_config'], [], browser) - - service['_fullTitle'] = 'foo - bar' - service['_failReasons'] = ['I am failure'] - await service.after(1) - - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, - { - status: 'failed', - name: 'foo - bar', - reason: 'I am failure' - }) - expect(got.put).toHaveBeenCalledWith( - 'https://api.browserstack.com/automate/sessions/session123.json', - { json: { - status: 'failed', - name: 'foo - bar', - reason: 'I am failure' - }, username: 'foo', password: 'bar' }) - }) - - describe('Cucumber only', function () { - it('should call _update with status "failed" if strict mode is "on" and all tests are pending', async () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: true } } as any) - - const updateSpy = jest.spyOn(service, '_update') - - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something but pending 1' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something but pending 2' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something but pending 3' }, result: { status: 'PENDING' } }) - - await service.after(1) - - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: 'Some steps/hooks are pending for scenario "Can do something but pending 1"' + '\n' + - 'Some steps/hooks are pending for scenario "Can do something but pending 2"' + '\n' + - 'Some steps/hooks are pending for scenario "Can do something but pending 3"', - status: 'failed', - }) - expect(updateSpy).toHaveBeenCalled() - }) - - it('should call _update with status "passed" when strict mode is "off" and only passed and pending tests ran', async () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - - const updateSpy = jest.spyOn(service, '_update') - - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something' }, result: { status: 'PASSED' } }) - await service.afterScenario({ pickle: { name: 'Can do something' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something' }, result: { status: 'PASSED' } }) - - await service.after(0) - - expect(updateSpy).toHaveBeenCalled() - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: undefined, - status: 'passed', - }) - }) - - it('should call _update with status is "failed" when strict mode is "on" and only passed and pending tests ran', async () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: true } } as any) - - const updateSpy = jest.spyOn(service, '_update') - - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something 1' }, result: { status: 'PASSED' } }) - await service.afterScenario({ pickle: { name: 'Can do something but pending' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something 2' }, result: { status: 'PASSED' } }) - - await service.after(1) - - expect(updateSpy).toHaveBeenCalled() - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: 'Some steps/hooks are pending for scenario "Can do something but pending"', - status: 'failed', - }) - }) - - it('should call _update with status "passed" when all tests are skipped', async () => { - const updateSpy = jest.spyOn(service, '_update') - - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something skipped 1' }, result: { status: 'SKIPPED' } }) - await service.afterScenario({ pickle: { name: 'Can do something skipped 2' }, result: { status: 'SKIPPED' } }) - await service.afterScenario({ pickle: { name: 'Can do something skipped 3' }, result: { status: 'SKIPPED' } }) - - await service.after(0) - - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: undefined, - status: 'passed', - }) - }) - - it('should call _update with status "failed" when strict mode is "on" and only failed and pending tests ran', async () => { - service = new BrowserstackService({}, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: true } } as any) - - const updateSpy = jest.spyOn(service, '_update') - const afterSpy = jest.spyOn(service, 'after') - - await service.beforeSession(service['_config'] as any) - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, { - name: 'Feature1' - }) - - await service.afterScenario({ pickle: { name: 'Can do something failed 1' }, result: { message: 'I am error, hear me roar', status: 'FAILED' } }) - await service.afterScenario({ pickle: { name: 'Can do something but pending 2' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something but passed 3' }, result: { status: 'SKIPPED' } }) - - await service.after(1) - - expect(updateSpy).toHaveBeenCalledTimes(2) - expect(updateSpy).toHaveBeenLastCalledWith( - service['_browser']?.sessionId, { - name: 'Feature1', - reason: - 'I am error, hear me roar' + - '\n' + - 'Some steps/hooks are pending for scenario "Can do something but pending 2"', - status: 'failed', - }) - expect(afterSpy).toHaveBeenCalledTimes(1) - }) - - it('should call _update with status "failed" when strict mode is "off" and only failed and pending tests ran', async () => { - const updateSpy = jest.spyOn(service, '_update') - - await service.beforeSession(service['_config'] as any) - await service.before(service['_config'], [], browser) - await service.beforeFeature(null, { name: 'Feature1' }) - - expect(updateSpy).toHaveBeenCalledWith(service['_browser']?.sessionId, { - name: 'Feature1' - }) - - await service.afterScenario({ pickle: { name: 'Can do something failed 1' }, result: { message: 'I am error, hear me roar', status: 'FAILED' } }) - await service.afterScenario({ pickle: { name: 'Can do something but pending 2' }, result: { status: 'PENDING' } }) - await service.afterScenario({ pickle: { name: 'Can do something but passed 3' }, result: { status: 'SKIPPED' } }) - - await service.after(1) - - expect(updateSpy).toHaveBeenCalledTimes(2) - expect(updateSpy).toHaveBeenLastCalledWith( - service['_browser']?.sessionId, { - name: 'Feature1', - reason: 'I am error, hear me roar', - status: 'failed', - } - ) - }) - - describe('preferScenarioName', () => { - describe('enabled', () => { - [ - { status: 'FAILED', body: { - name: 'Feature1', - reason: 'Unknown Error', - status: 'failed', - } }, - { status: 'SKIPPED', body: { - name: 'Can do something single', - reason: undefined, - status: 'failed', - } } - /*, 5, 4, 0*/ - ].map(({ status, body }) => - it(`should call _update /w status failed and name of Scenario when single "${status}" Scenario ran`, async () => { - service = new BrowserstackService({ preferScenarioName : true }, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - service.before({}, [], browser) - - const updateSpy = jest.spyOn(service, '_update') - - await service.beforeFeature(null, { name: 'Feature1' }) - await service.afterScenario({ pickle: { name: 'Can do something single' }, result: { status } }) - await service.after(1) - - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, body) - }) - ) - - it('should call _update /w status passed and name of Scenario when single "passed" Scenario ran', async () => { - service = new BrowserstackService({ preferScenarioName : true }, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - service.before({}, [], browser) - - const updateSpy = jest.spyOn(service, '_update') - - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something single' }, result: { status: 'SKIPPED' } }) - - await service.after(0) - - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, { - name: 'Can do something single', - reason: undefined, - status: 'passed', - }) - }) - }) - - describe('disabled', () => { - ['FAILED', 'AMBIGUOUS', 'UNDEFINED', 'UNKNOWN'].map(status => - it(`should call _update /w status failed and name of Feature when single "${status}" Scenario ran`, async () => { - service = new BrowserstackService({ preferScenarioName : false }, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - service.before({}, [], browser) - - const updateSpy = jest.spyOn(service, '_update') - - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ pickle: { name: 'Can do something single' }, result: { status } }) - - await service.after(1) - - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: 'Unknown Error', - status: 'failed', - }) - }) - ) - - it('should call _update /w status passed and name of Feature when single "passed" Scenario ran', async () => { - service = new BrowserstackService({ preferScenarioName : false }, [] as any, - { user: 'foo', key: 'bar', cucumberOpts: { strict: false } } as any) - service.before({}, [], browser) - - const updateSpy = jest.spyOn(service, '_update') - - await service.beforeFeature(null, { name: 'Feature1' }) - - await service.afterScenario({ - pickle: { name: 'Can do something single' }, - result: { status: 'PASSED' } - }) - await service.after(0) - - expect(updateSpy).toHaveBeenLastCalledWith(service['_browser']?.sessionId, { - name: 'Feature1', - reason: undefined, - status: 'passed', - }) - }) - }) - }) - }) -}) diff --git a/tests/util.test.ts b/tests/util.test.ts deleted file mode 100644 index 6ee3179..0000000 --- a/tests/util.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { Browser, MultiRemoteBrowser } from 'webdriverio' - -import { - getBrowserDescription, - getBrowserCapabilities, - isBrowserstackCapability, - getParentSuiteName -} from '../src/util' - -describe('getBrowserCapabilities', () => { - it('should get default browser capabilities', () => { - const browser = { - capabilities: { - browser: 'browser' - } - } as Browser - expect(getBrowserCapabilities(browser)) - .toEqual(browser.capabilities as any) - }) - - it('should get multiremote browser capabilities', () => { - const browser = { - isMultiremote: true, - browserA: { - capabilities: { - browser: 'browser' - } - } - } as any as MultiRemoteBrowser - expect(getBrowserCapabilities(browser, {}, 'browserA')) - .toEqual(browser.browserA.capabilities as any) - }) - - it('should handle null multiremote browser capabilities', () => { - const browser = { - isMultiremote: true, - browserA: {} - } as any as MultiRemoteBrowser - expect(getBrowserCapabilities(browser, {}, 'browserB')).toEqual({}) - }) - - it('should merge service capabilities and browser capabilities', () => { - const browser = { - capabilities: { - browser: 'browser', - os: 'OS X', - } - } as any as Browser - expect(getBrowserCapabilities(browser, { os: 'Windows' })) - .toEqual({ os:'Windows', browser: 'browser' } as any) - }) - - it('should merge multiremote service capabilities and browser capabilities', () => { - const browser = { - isMultiremote: true, - browserA: { - capabilities: { - browser: 'browser', - os: 'OS X', - } - } - } as any as MultiRemoteBrowser - expect(getBrowserCapabilities(browser, { browserA: { capabilities: { os: 'Windows' } } }, 'browserA')) - .toEqual({ os:'Windows', browser: 'browser' } as any) - }) - - it('should handle null multiremote browser capabilities', () => { - const browser = { - isMultiremote: true, - browserA: {} - } as any as MultiRemoteBrowser - expect(getBrowserCapabilities(browser, {}, 'browserB')) - .toEqual({}) - }) - - it('should handle null multiremote browser capabilities', () => { - const browser = { - isMultiremote: true, - browserA: {} - } as any as MultiRemoteBrowser - expect(getBrowserCapabilities(browser, { browserB: {} } as any, 'browserB')) - .toEqual({}) - }) -}) - -describe('getBrowserDescription', () => { - const defaultCap = { - 'device': 'device', - 'os': 'os', - 'osVersion': 'osVersion', - 'browserName': 'browserName', - 'browser': 'browser', - 'browserVersion': 'browserVersion', - } - const defaultDesc = 'device os osVersion browserName browser browserVersion' - const legacyCap = { - 'os_version': 'os_version', - 'browser_version': 'browser_version' - } - - it('should get correct description for default capabilities', () => { - expect(getBrowserDescription(defaultCap)).toEqual(defaultDesc) - }) - - it('should get correct description for legacy capabilities', () => { - expect(getBrowserDescription(legacyCap)).toEqual('os_version browser_version') - }) - - it('should get correct description for W3C capabilities', () => { - expect(getBrowserDescription({ 'bstack:options': defaultCap })).toEqual(defaultDesc) - }) - - it('should merge W3C and lecacy capabilities', () => { - expect(getBrowserDescription({ 'bstack:options': defaultCap })).toEqual(defaultDesc) - }) - - it('should not crash when capabilities is null or undefined', () => { - // @ts-expect-error test invalid params - expect(getBrowserDescription(undefined)).toEqual('') - // @ts-expect-error test invalid params - expect(getBrowserDescription(null)).toEqual('') - }) -}) - -describe('isBrowserstackCapability', () => { - it('should detect browserstack W3C capabilities', () => { - expect(isBrowserstackCapability({})).toBe(false) - expect(isBrowserstackCapability()).toBe(false) - // @ts-expect-error test invalid params - expect(isBrowserstackCapability({ 'bstack:options': null })).toBe(false) - expect(isBrowserstackCapability({ 'bstack:options': {} })).toBe(true) - }) -}) - -describe('getParentSuiteName', () => { - it('should return the parent suite name', () => { - expect(getParentSuiteName('foo bar', 'foo')).toBe('foo') - expect(getParentSuiteName('foo', 'foo bar')).toBe('foo') - expect(getParentSuiteName('foo bar', 'foo baz')).toBe('foo') - expect(getParentSuiteName('foo bar', 'foo bar')).toBe('foo bar') - }) - - it('should return empty string if no common parent', () => { - expect(getParentSuiteName('foo bar', 'baz bar')).toBe('') - }) - - it('should handle empty values', () => { - expect(getParentSuiteName('', 'foo')).toBe('') - expect(getParentSuiteName('foo', '')).toBe('') - }) -}) diff --git a/tsconfig.prod.json b/tsconfig.prod.json deleted file mode 100644 index 9b530b7..0000000 --- a/tsconfig.prod.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": "@tsconfig/node12/tsconfig.json", - - "compilerOptions": { - "allowJs": true, - "declaration": true, - "declarationMap": true, - "resolveJsonModule": true, - "removeComments": false, - "strictFunctionTypes": false, - "esModuleInterop": true, - "lib": ["dom", "es2019"], - "types": [ - "node" - ], - "baseUrl": ".", - "outDir": "./build", - "rootDir": "./src" - }, - - "include": [ - "src/**/*", - "../../@types" - ], - - "exclude": [ - "node_modules", - "coverage", - ] -} From f4a2f131aad5d4ff684004ae13031cbded667647 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Wed, 13 Aug 2025 15:10:05 +0530 Subject: [PATCH 4/8] Update package name, version and add .nvmrc --- .nvmrc | 1 + package-lock.json | 20 ++++++++++---------- package.json | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..df93858 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.11.0 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7ad12cd..6219334 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "browserstack-proto", - "version": "1.0.0", + "name": "@browserstack/wdio-browserstack-service", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "browserstack-proto", - "version": "1.0.0", + "name": "@browserstack/wdio-browserstack-service", + "version": "2.0.0", "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.5.2", @@ -252,9 +252,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@types/node": { - "version": "24.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", - "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", "dependencies": { "undici-types": "~7.10.0" } @@ -447,9 +447,9 @@ } }, "node_modules/ts-proto": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.7.5.tgz", - "integrity": "sha512-FoRxSaNW+P3m+GiXIZjUjhaHXT67Ah4zMGKzn4yklbGRQTS+PqpUhKo5AJnwfUDUByjEUG7ch36byFUYWRH9Nw==", + "version": "2.7.7", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.7.7.tgz", + "integrity": "sha512-/OfN9/Yriji2bbpOysZ/Jzc96isOKz+eBTJEcKaIZ0PR6x1TNgVm4Lz0zfbo+J0jwFO7fJjJyssefBPQ0o1V9A==", "dev": true, "dependencies": { "@bufbuild/protobuf": "^2.0.0", diff --git a/package.json b/package.json index 99b5400..d76e714 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "browserstack-proto", - "version": "1.0.0", + "name": "@browserstack/wdio-browserstack-service", + "version": "2.0.0", "description": "Protobuf definitions for BrowserStack SDK", "type": "module", "main": "dist/index.js", From 48f7e2f48383bc0570fe3b0591ef9a6cc8e2e478 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Wed, 13 Aug 2025 15:17:15 +0530 Subject: [PATCH 5/8] SDK-3773: Update package.json --- package.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d76e714..41685bb 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,18 @@ { "name": "@browserstack/wdio-browserstack-service", "version": "2.0.0", - "description": "Protobuf definitions for BrowserStack SDK", + "description": "WebdriverIO service for better Browserstack integration", + "author": "Browserstack", + "homepage": "https://github.com/browserstack/wdio-browserstack-service", "type": "module", "main": "dist/index.js", + "engines": { + "node": ">=16.0.0" + }, + "repository": { + "type": "git", + "url": "git://github.com/browserstack/wdio-browserstack-service.git" + }, "types": "dist/index.d.ts", "files": [ "dist/**/*", @@ -22,7 +31,9 @@ "grpc", "webdriverio" ], - "author": "BrowserStack", + "bugs": { + "url": "https://github.com/browserstack/wdio-browserstack-service/issues" + }, "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.5.2", From 166174369db35f9bcfe48428b186d6102c0d1db5 Mon Sep 17 00:00:00 2001 From: xxshubhamxx Date: Wed, 13 Aug 2025 15:20:30 +0530 Subject: [PATCH 6/8] SDK-3773: Add new line at EOF --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 780a005..7feec19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules dist -generated \ No newline at end of file +generated From f963689a6699b4b4665dae268a90907515594508 Mon Sep 17 00:00:00 2001 From: Shubham Garg <74714209+xxshubhamxx@users.noreply.github.com> Date: Mon, 18 Aug 2025 18:49:54 +0530 Subject: [PATCH 7/8] Handled comments --- .nvmrc | 2 +- CODEOWNERS | 1 + README.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 CODEOWNERS create mode 100644 README.md diff --git a/.nvmrc b/.nvmrc index df93858..7ea6a59 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.11.0 \ No newline at end of file +v20.11.0 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..6487822 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @browserstack/default_owner diff --git a/README.md b/README.md new file mode 100644 index 0000000..8fbc9a1 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Source code for the @browserstack/wdio-browserstack-service npm module \ No newline at end of file From 021e7e0649d277cc947363a60a648db2c91ff1a9 Mon Sep 17 00:00:00 2001 From: Shubham Garg <74714209+xxshubhamxx@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:12:13 +0530 Subject: [PATCH 8/8] Update CODEOWNERS --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6487822..0b4ea4d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @browserstack/default_owner +* @browserstack/sdk-dev