feat(launch): connect to process.env.SELENIUM_REMOTE_URL instead of local launch (#9056)

This commit is contained in:
Dmitry Gozman 2021-09-22 21:13:32 -07:00 коммит произвёл GitHub
Родитель 1bebbc31b2
Коммит 2afe76d86d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 603 добавлений и 46 удалений

311
package-lock.json сгенерированный
Просмотреть файл

@ -82,6 +82,7 @@
"ansi-to-html": "^0.7.1",
"babel-loader": "^8.2.2",
"chokidar": "^3.5.0",
"chromedriver": "^93.0.1",
"commonmark": "^0.29.1",
"concurrently": "^6.2.1",
"cross-env": "^7.0.2",
@ -1358,6 +1359,12 @@
"node": ">=6"
}
},
"node_modules/@testim/chrome-version": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.0.7.tgz",
"integrity": "sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw==",
"dev": true
},
"node_modules/@types/anymatch": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
@ -2123,6 +2130,19 @@
"node": ">= 6.0.0"
}
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"dependencies": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -2379,6 +2399,15 @@
"node": ">= 4.5.0"
}
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/babel-loader": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
@ -3064,6 +3093,28 @@
"node": ">=6.0"
}
},
"node_modules/chromedriver": {
"version": "93.0.1",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-93.0.1.tgz",
"integrity": "sha512-KDzbW34CvQLF5aTkm3b5VdlTrvdIt4wEpCzT2p4XJIQWQZEPco5pNce7Lu9UqZQGkhQ4mpZt4Ky6NKVyIS2N8A==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@testim/chrome-version": "^1.0.7",
"axios": "^0.21.2",
"del": "^6.0.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "^5.0.0",
"proxy-from-env": "^1.1.0",
"tcp-port-used": "^1.0.1"
},
"bin": {
"chromedriver": "bin/chromedriver"
},
"engines": {
"node": ">=10"
}
},
"node_modules/cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@ -3113,6 +3164,15 @@
"node": ">= 4.0"
}
},
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/cli": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
@ -3899,6 +3959,28 @@
"node": ">=0.10.0"
}
},
"node_modules/del": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz",
"integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==",
"dev": true,
"dependencies": {
"globby": "^11.0.1",
"graceful-fs": "^4.2.4",
"is-glob": "^4.0.1",
"is-path-cwd": "^2.2.0",
"is-path-inside": "^3.0.2",
"p-map": "^4.0.0",
"rimraf": "^3.0.2",
"slash": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@ -5222,6 +5304,26 @@
"readable-stream": "^2.3.6"
}
},
"node_modules/follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -5987,6 +6089,15 @@
"node": ">=0.8.19"
}
},
"node_modules/indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@ -6022,6 +6133,15 @@
"node": ">= 0.10"
}
},
"node_modules/ip-regex": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz",
"integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@ -6244,6 +6364,24 @@
"node": ">= 0.4"
}
},
"node_modules/is-path-cwd": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
"integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@ -6290,6 +6428,12 @@
"node": ">= 0.4"
}
},
"node_modules/is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
"dev": true
},
"node_modules/is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@ -6308,6 +6452,20 @@
"node": ">=4"
}
},
"node_modules/is2": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz",
"integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==",
"dev": true,
"dependencies": {
"deep-is": "^0.1.3",
"ip-regex": "^4.1.0",
"is-url": "^1.2.4"
},
"engines": {
"node": ">=v0.10.0"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -7454,6 +7612,21 @@
"node": ">=6"
}
},
"node_modules/p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"dependencies": {
"aggregate-error": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@ -9258,6 +9431,16 @@
"node": ">=6"
}
},
"node_modules/tcp-port-used": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz",
"integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==",
"dev": true,
"dependencies": {
"debug": "4.3.1",
"is2": "^2.0.6"
}
},
"node_modules/terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
@ -11626,6 +11809,12 @@
"defer-to-connect": "^1.0.1"
}
},
"@testim/chrome-version": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.0.7.tgz",
"integrity": "sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw==",
"dev": true
},
"@types/anymatch": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
@ -12296,6 +12485,16 @@
"debug": "4"
}
},
"aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"requires": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
}
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -12509,6 +12708,15 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dev": true,
"requires": {
"follow-redirects": "^1.14.0"
}
},
"babel-loader": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
@ -13086,6 +13294,21 @@
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
"dev": true
},
"chromedriver": {
"version": "93.0.1",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-93.0.1.tgz",
"integrity": "sha512-KDzbW34CvQLF5aTkm3b5VdlTrvdIt4wEpCzT2p4XJIQWQZEPco5pNce7Lu9UqZQGkhQ4mpZt4Ky6NKVyIS2N8A==",
"dev": true,
"requires": {
"@testim/chrome-version": "^1.0.7",
"axios": "^0.21.2",
"del": "^6.0.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "^5.0.0",
"proxy-from-env": "^1.1.0",
"tcp-port-used": "^1.0.1"
}
},
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@ -13128,6 +13351,12 @@
"source-map": "~0.6.0"
}
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true
},
"cli": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
@ -13756,6 +13985,22 @@
}
}
},
"del": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz",
"integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==",
"dev": true,
"requires": {
"globby": "^11.0.1",
"graceful-fs": "^4.2.4",
"is-glob": "^4.0.1",
"is-path-cwd": "^2.2.0",
"is-path-inside": "^3.0.2",
"p-map": "^4.0.0",
"rimraf": "^3.0.2",
"slash": "^3.0.0"
}
},
"des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@ -14860,6 +15105,12 @@
"readable-stream": "^2.3.6"
}
},
"follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
"dev": true
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -15483,6 +15734,12 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true
},
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@ -15515,6 +15772,12 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true
},
"ip-regex": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz",
"integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==",
"dev": true
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@ -15680,6 +15943,18 @@
"integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
"dev": true
},
"is-path-cwd": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
"integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
"dev": true
},
"is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true
},
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@ -15714,6 +15989,12 @@
"has-symbols": "^1.0.2"
}
},
"is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
"dev": true
},
"is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@ -15726,6 +16007,17 @@
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
"dev": true
},
"is2": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz",
"integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==",
"dev": true,
"requires": {
"deep-is": "^0.1.3",
"ip-regex": "^4.1.0",
"is-url": "^1.2.4"
}
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -16670,6 +16962,15 @@
"p-limit": "^2.0.0"
}
},
"p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@ -18144,6 +18445,16 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"tcp-port-used": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz",
"integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==",
"dev": true,
"requires": {
"debug": "4.3.1",
"is2": "^2.0.6"
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",

Просмотреть файл

@ -111,6 +111,7 @@
"ansi-to-html": "^0.7.1",
"babel-loader": "^8.2.2",
"chokidar": "^3.5.0",
"chromedriver": "^93.0.1",
"commonmark": "^0.29.1",
"concurrently": "^6.2.1",
"cross-env": "^7.0.2",

Просмотреть файл

@ -57,6 +57,9 @@ export abstract class BrowserType extends SdkObject {
const controller = new ProgressController(metadata, this);
controller.setLogName('browser');
const browser = await controller.run(progress => {
const seleniumHubUrl = (options as any).__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
if (seleniumHubUrl)
return this._launchWithSeleniumHub(progress, seleniumHubUrl, options);
return this._innerLaunchWithRetries(progress, options, undefined, helper.debugProtocolLogger(protocolLogger)).catch(e => { throw this._rewriteStartupError(e); });
}, TimeoutSettings.timeout(options));
return browser;
@ -243,6 +246,10 @@ export abstract class BrowserType extends SdkObject {
throw new Error('CDP connections are only supported by Chromium');
}
async _launchWithSeleniumHub(progress: Progress, hubUrl: string, options: types.LaunchOptions): Promise<Browser> {
throw new Error('Connecting to SELENIUM_REMOTE_URL is only supported by Chromium');
}
private _validateLaunchOptions<Options extends types.LaunchOptions>(options: Options): Options {
const { devtools = false } = options;
let { headless = !devtools, downloadsPath, proxy } = options;

Просмотреть файл

@ -27,9 +27,9 @@ import { ConnectionTransport, ProtocolRequest, WebSocketTransport } from '../tra
import { CRDevTools } from './crDevTools';
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import * as types from '../types';
import { debugMode, headersArrayToObject, removeFolders } from '../../utils/utils';
import { debugMode, fetchData, headersArrayToObject, removeFolders, streamToString } from '../../utils/utils';
import { RecentLogsCollector } from '../../utils/debugLogger';
import { ProgressController } from '../progress';
import { Progress, ProgressController } from '../progress';
import { TimeoutSettings } from '../../utils/timeoutSettings';
import { helper } from '../helper';
import { CallMetadata } from '../instrumentation';
@ -52,42 +52,45 @@ export class Chromium extends BrowserType {
override async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, headers?: types.HeadersArray }, timeout?: number) {
const controller = new ProgressController(metadata, this);
controller.setLogName('browser');
const browserLogsCollector = new RecentLogsCollector();
return controller.run(async progress => {
let headersMap: { [key: string]: string; } | undefined;
if (options.headers)
headersMap = headersArrayToObject(options.headers, false);
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
const chromeTransport = await WebSocketTransport.connect(progress, await urlToWSEndpoint(endpointURL), headersMap);
const browserProcess: BrowserProcess = {
close: async () => {
await removeFolders([ artifactsDir ]);
await chromeTransport.closeAndWait();
},
kill: async () => {
await removeFolders([ artifactsDir ]);
await chromeTransport.closeAndWait();
}
};
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
slowMo: options.slowMo,
name: 'chromium',
isChromium: true,
persistent: { noDefaultViewport: true },
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector,
artifactsDir,
downloadsPath: artifactsDir,
tracesDir: artifactsDir
};
return await CRBrowser.connect(chromeTransport, browserOptions);
return await this._connectOverCDPInternal(progress, endpointURL, options);
}, TimeoutSettings.timeout({timeout}));
}
async _connectOverCDPInternal(progress: Progress, endpointURL: string, options: { slowMo?: number, headers?: types.HeadersArray }, onClose?: () => Promise<void>) {
let headersMap: { [key: string]: string; } | undefined;
if (options.headers)
headersMap = headersArrayToObject(options.headers, false);
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
const wsEndpoint = await urlToWSEndpoint(endpointURL);
progress.throwIfAborted();
const chromeTransport = await WebSocketTransport.connect(progress, wsEndpoint, headersMap);
const doClose = async () => {
await removeFolders([ artifactsDir ]);
await chromeTransport.closeAndWait();
await onClose?.();
};
const browserProcess: BrowserProcess = { close: doClose, kill: doClose };
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
slowMo: options.slowMo,
name: 'chromium',
isChromium: true,
persistent: { noDefaultViewport: true },
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector: new RecentLogsCollector(),
artifactsDir,
downloadsPath: artifactsDir,
tracesDir: artifactsDir
};
progress.throwIfAborted();
return await CRBrowser.connect(chromeTransport, browserOptions);
}
private _createDevTools() {
// TODO: this is totally wrong when using channels.
const directory = registry.findExecutable('chromium').directory;
@ -128,7 +131,77 @@ export class Chromium extends BrowserType {
transport.send(message);
}
override async _launchWithSeleniumHub(progress: Progress, hubUrl: string, options: types.LaunchOptions): Promise<CRBrowser> {
if (!hubUrl.endsWith('/'))
hubUrl = hubUrl + '/';
const args = this._innerDefaultArgs(options);
args.push('--remote-debugging-port=0');
const desiredCapabilities = { 'browserName': 'chrome', 'goog:chromeOptions': { args } };
progress.log(`<connecting to selenium> ${hubUrl}`);
const response = await fetchData({
url: hubUrl + 'session',
method: 'POST',
data: JSON.stringify({
desiredCapabilities,
capabilities: { alwaysMatch: desiredCapabilities }
}),
timeout: progress.timeUntilDeadline(),
}, async response => {
const body = await streamToString(response);
let message = '';
try {
const json = JSON.parse(body);
message = json.value.localizedMessage || json.value.message;
} catch (e) {
}
return new Error(`Error connecting to Selenium at ${hubUrl}: ${message}`);
});
const value = JSON.parse(response).value;
const sessionId = value.sessionId;
progress.log(`<connected to selenium> sessionId=${sessionId}`);
const disconnectFromSelenium = async () => {
progress.log(`<disconnecting from selenium> sessionId=${sessionId}`);
await fetchData({
url: hubUrl + 'session/' + sessionId,
method: 'DELETE',
}).catch(error => progress.log(`<error disconnecting from selenium>: ${error}`));
progress.log(`<disconnected from selenium> sessionId=${sessionId}`);
};
try {
const capabilities = value.capabilities;
const maybeChromeOptions = capabilities['goog:chromeOptions'];
const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === 'object' ? maybeChromeOptions : undefined;
const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === 'string' ? chromeOptions.debuggerAddress : undefined;
const chromeOptionsURL = typeof maybeChromeOptions === 'string' ? maybeChromeOptions : undefined;
let endpointURL = capabilities['se:cdp'] || debuggerAddress || chromeOptionsURL;
if (!['ws://', 'wss://', 'http://', 'https://'].some(protocol => endpointURL.startsWith(protocol)))
endpointURL = 'http://' + endpointURL;
return this._connectOverCDPInternal(progress, endpointURL, { slowMo: options.slowMo }, disconnectFromSelenium);
} catch (e) {
await disconnectFromSelenium();
throw e;
}
}
_defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
const chromeArguments = this._innerDefaultArgs(options);
chromeArguments.push(`--user-data-dir=${userDataDir}`);
if (options.useWebSocket)
chromeArguments.push('--remote-debugging-port=0');
else
chromeArguments.push('--remote-debugging-pipe');
if (isPersistent)
chromeArguments.push('about:blank');
else
chromeArguments.push('--no-startup-window');
return chromeArguments;
}
private _innerDefaultArgs(options: types.LaunchOptions): string[] {
const { args = [], proxy } = options;
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
if (userDataDirArg)
@ -138,16 +211,11 @@ export class Chromium extends BrowserType {
if (args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const chromeArguments = [...DEFAULT_ARGS];
chromeArguments.push(`--user-data-dir=${userDataDir}`);
// See https://github.com/microsoft/playwright/issues/7362
if (os.platform() === 'darwin')
chromeArguments.push('--enable-use-zoom-for-dsf=false');
if (options.useWebSocket)
chromeArguments.push('--remote-debugging-port=0');
else
chromeArguments.push('--remote-debugging-pipe');
if (options.devtools)
chromeArguments.push('--auto-open-devtools-for-tabs');
if (options.headless) {
@ -179,10 +247,6 @@ export class Chromium extends BrowserType {
chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
}
chromeArguments.push(...args);
if (isPersistent)
chromeArguments.push('about:blank');
else
chromeArguments.push('--no-startup-window');
return chromeArguments;
}
}

Просмотреть файл

@ -44,6 +44,7 @@ type HTTPRequestParams = {
method?: string,
headers?: http.OutgoingHttpHeaders,
data?: string | Buffer,
timeout?: number,
};
function httpRequest(params: HTTPRequestParams, onResponse: (r: http.IncomingMessage) => void, onError: (error: Error) => void) {
@ -81,14 +82,26 @@ function httpRequest(params: HTTPRequestParams, onResponse: (r: http.IncomingMes
https.request(options, requestCallback) :
http.request(options, requestCallback);
request.on('error', onError);
if (params.timeout !== undefined) {
const rejectOnTimeout = () => {
onError(new Error(`Request to ${params.url} timed out after ${params.timeout}ms`));
request.abort();
};
if (params.timeout <= 0) {
rejectOnTimeout();
return;
}
request.setTimeout(params.timeout, rejectOnTimeout);
}
request.end(params.data);
}
export function fetchData(params: HTTPRequestParams): Promise<string> {
export function fetchData(params: HTTPRequestParams, onError?: (response: http.IncomingMessage) => Promise<Error>): Promise<string> {
return new Promise((resolve, reject) => {
httpRequest(params, response => {
httpRequest(params, async response => {
if (response.statusCode !== 200) {
reject(new Error(`fetch failed: server returned code ${response.statusCode}. URL: ${params.url}`));
const error = onError ? await onError(response) : new Error(`fetch failed: server returned code ${response.statusCode}. URL: ${params.url}`);
reject(error);
return;
}
let body = '';
@ -426,3 +439,12 @@ export function wrapInASCIIBox(text: string, padding = 0): string {
export function isFilePayload(value: any): boolean {
return typeof value === 'object' && value['name'] && value['mimeType'] && value['buffer'];
}
export function streamToString(stream: stream.Readable): Promise<string> {
return new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
});
}

Просмотреть файл

@ -0,0 +1,4 @@
Download locations:
- https://github.com/SeleniumHQ/selenium/releases/download/selenium-3.141.59/selenium-server-standalone-3.141.59.jar
- https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-server-4.0.0-rc-1.jar

Просмотреть файл

@ -0,0 +1,3 @@
#!/usr/bin/env node
process.exit(1);

Просмотреть файл

@ -0,0 +1,19 @@
{
"capabilities":
[
{
"browserName": "chrome",
"maxInstances": 5,
"seleniumProtocol": "WebDriver"
}
],
"role": "standalone",
"port": 4444,
"debug": false,
"browserTimeout": 0,
"timeout": 1800,
"enablePassThrough": true,
"server": {
"port": 4444
}
}

Двоичные данные
tests/assets/selenium-grid/selenium-server-4.0.0-rc-1.jar Normal file

Двоичный файл не отображается.

Двоичные данные
tests/assets/selenium-grid/selenium-server-standalone-3.141.59.jar Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,107 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { playwrightTest as test, expect } from './config/browserTest';
import type { TestInfo } from '../types/test';
import path from 'path';
import fs from 'fs';
const chromeDriver = require('chromedriver').path;
const brokenDriver = path.join(__dirname, 'assets', 'selenium-grid', 'broken-selenium-driver.js');
const seleniumConfigStandalone = path.join(__dirname, 'assets', 'selenium-grid', 'selenium-config-standalone.json');
const standalone_3_141_59 = path.join(__dirname, 'assets', 'selenium-grid', 'selenium-server-standalone-3.141.59.jar');
const selenium_4_0_0_rc1 = path.join(__dirname, 'assets', 'selenium-grid', 'selenium-server-4.0.0-rc-1.jar');
function writeSeleniumConfig(testInfo: TestInfo, port: number) {
const content = fs.readFileSync(seleniumConfigStandalone, 'utf8').replace(/4444/g, String(port));
const file = testInfo.outputPath('selenium-config.json');
fs.writeFileSync(file, content, 'utf8');
return file;
}
test.skip(({ mode }) => mode !== 'default', 'Using test hooks');
test.skip(() => !!process.env.INSIDE_DOCKER, 'Docker image does not have Java');
test('selenium grid 3.141.59 standalone chromium', async ({ browserOptions, browserName, childProcess, waitForPort, browserType }, testInfo) => {
test.skip(browserName !== 'chromium');
const port = testInfo.workerIndex + 15123;
const grid = childProcess({
command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', standalone_3_141_59, '-config', writeSeleniumConfig(testInfo, port)],
cwd: __dirname,
});
await waitForPort(port);
const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`;
const browser = await browserType.launch({ ...browserOptions, __testHookSeleniumRemoteURL } as any);
const page = await browser.newPage();
await page.setContent('<title>Hello world</title><div>Get Started</div>');
await page.click('text=Get Started');
await expect(page).toHaveTitle('Hello world');
await browser.close();
expect(grid.output).toContain('Starting ChromeDriver');
expect(grid.output).toContain('Started new session');
await grid.waitForOutput('Removing session');
});
test('selenium grid 4.0.0-rc-1 standalone chromium', async ({ browserOptions, browserName, childProcess, waitForPort, browserType }, testInfo) => {
test.skip(browserName !== 'chromium');
const port = testInfo.workerIndex + 15123;
const grid = childProcess({
command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', selenium_4_0_0_rc1, 'standalone', '--config', writeSeleniumConfig(testInfo, port)],
cwd: __dirname,
});
await waitForPort(port);
const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`;
const browser = await browserType.launch({ ...browserOptions, __testHookSeleniumRemoteURL } as any);
const page = await browser.newPage();
await page.setContent('<title>Hello world</title><div>Get Started</div>');
await page.click('text=Get Started');
await expect(page).toHaveTitle('Hello world');
await browser.close();
expect(grid.output).toContain('Starting ChromeDriver');
expect(grid.output).toContain('Session created');
await grid.waitForOutput('Deleted session');
});
test('selenium grid 4.0.0-rc-1 standalone chromium broken driver', async ({ browserOptions, browserName, childProcess, waitForPort, browserType }, testInfo) => {
test.skip(browserName !== 'chromium');
const port = testInfo.workerIndex + 15123;
const grid = childProcess({
command: ['java', `-Dwebdriver.chrome.driver=${brokenDriver}`, '-jar', selenium_4_0_0_rc1, 'standalone', '--config', writeSeleniumConfig(testInfo, port)],
cwd: __dirname,
});
await waitForPort(port);
const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`;
const error = await browserType.launch({ ...browserOptions, __testHookSeleniumRemoteURL } as any).catch(e => e);
expect(error.message).toContain(`Error connecting to Selenium at http://localhost:${port}/wd/hub/: Could not start a new session`);
expect(grid.output).not.toContain('Starting ChromeDriver');
});
test('selenium grid 3.141.59 standalone non-chromium', async ({ browserName, browserType }, testInfo) => {
test.skip(browserName === 'chromium');
const __testHookSeleniumRemoteURL = `http://localhost:4444/wd/hub`;
const error = await browserType.launch({ __testHookSeleniumRemoteURL } as any).catch(e => e);
expect(error.message).toContain('Connecting to SELENIUM_REMOTE_URL is only supported by Chromium');
});

Просмотреть файл

@ -34,6 +34,8 @@ export class TestChildProcess {
exited: Promise<{ exitCode: number | null, signal: string | null }>;
exitCode: Promise<number>;
private _outputCallbacks = new Set<() => void>();
constructor(params: TestChildParams) {
this.params = params;
this.process = spawn(params.command[0], params.command.slice(1), {
@ -53,6 +55,9 @@ export class TestChildProcess {
if (process.env.PWTEST_DEBUG)
process.stdout.write(String(chunk));
this.onOutput?.();
for (const cb of this._outputCallbacks)
cb();
this._outputCallbacks.clear();
};
this.process.stderr.on('data', appendChunk);
@ -91,6 +96,11 @@ export class TestChildProcess {
if (r.signal)
throw new Error(`Process recieved signal: ${r.signal}`);
}
async waitForOutput(substring: string) {
while (!this.output.includes(substring))
await new Promise<void>(f => this._outputCallbacks.add(f));
}
}
export type CommonFixtures = {

Просмотреть файл

@ -87,6 +87,15 @@ Example:
process.stdout.write(execSync('npm run --silent doc'));
} catch (e) {
}
if (browserName === 'chromium') {
// 5. Update chromedriver.
console.log('\nUpdating chromedriver...');
try {
process.stdout.write(execSync('npm install --save-dev chromedriver@latest'));
} catch (e) {
}
}
}
console.log(`\nRolled ${browserName} to ${revision}`);
})().catch(err => {