|
| 1 | +const base = require("@playwright/test"); |
| 2 | +const cp = require("child_process"); |
| 3 | +const { _android } = require("playwright"); |
| 4 | +const clientPlaywrightVersion = cp |
| 5 | + .execSync("npx playwright --version") |
| 6 | + .toString() |
| 7 | + .trim() |
| 8 | + .split(" ")[1]; |
| 9 | +const BrowserStackLocal = require("browserstack-local"); |
| 10 | +const util = require("util"); |
| 11 | + |
| 12 | +// BrowserStack Specific Capabilities. |
| 13 | +// Set 'browserstack.local:true For Local testing |
| 14 | +const caps = { |
| 15 | + osVersion: "13.0", |
| 16 | + deviceName: "Samsung Galaxy S23", // "Samsung Galaxy S22 Ultra", "Google Pixel 7 Pro", "OnePlus 9", etc. |
| 17 | + browserName: "chrome", |
| 18 | + realMobile: "true", |
| 19 | + name: "My android playwright test", |
| 20 | + build: "playwright-build-1", |
| 21 | + "browserstack.username": process.env.BROWSERSTACK_USERNAME || "<USERNAME>", |
| 22 | + "browserstack.accessKey": |
| 23 | + process.env.BROWSERSTACK_ACCESS_KEY || "<ACCESS_KEY>", |
| 24 | + "browserstack.local": process.env.BROWSERSTACK_LOCAL || false, |
| 25 | +}; |
| 26 | + |
| 27 | +exports.bsLocal = new BrowserStackLocal.Local(); |
| 28 | + |
| 29 | +// replace YOUR_ACCESS_KEY with your key. You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY". |
| 30 | +exports.BS_LOCAL_ARGS = { |
| 31 | + key: process.env.BROWSERSTACK_ACCESS_KEY || "ACCESSKEY", |
| 32 | +}; |
| 33 | + |
| 34 | +// Patching the capabilities dynamically according to the project name. |
| 35 | +const patchMobileCaps = (name, title) => { |
| 36 | + let combination = name.split(/@browserstack/)[0]; |
| 37 | + let [browerCaps, osCaps] = combination.split(/:/); |
| 38 | + let [browser, deviceName] = browerCaps.split(/@/); |
| 39 | + let osCapsSplit = osCaps.split(/ /); |
| 40 | + let os = osCapsSplit.shift(); |
| 41 | + let osVersion = osCapsSplit.join(" "); |
| 42 | + caps.browser = browser ? browser : "chrome"; |
| 43 | + caps.deviceName = deviceName ? deviceName : "Samsung Galaxy S22 Ultra"; |
| 44 | + caps.osVersion = osVersion ? osVersion : "12.0"; |
| 45 | + caps.name = title; |
| 46 | + caps.realMobile = "true"; |
| 47 | +}; |
| 48 | + |
| 49 | +const patchCaps = (name, title) => { |
| 50 | + let combination = name.split(/@browserstack/)[0]; |
| 51 | + let [browerCaps, osCaps] = combination.split(/:/); |
| 52 | + let [browser, browser_version] = browerCaps.split(/@/); |
| 53 | + let osCapsSplit = osCaps.split(/ /); |
| 54 | + let os = osCapsSplit.shift(); |
| 55 | + let os_version = osCapsSplit.join(" "); |
| 56 | + caps.browser = browser ? browser : "chrome"; |
| 57 | + caps.browser_version = browser_version ? browser_version : "latest"; |
| 58 | + caps.os = os ? os : "osx"; |
| 59 | + caps.os_version = os_version ? os_version : "catalina"; |
| 60 | + caps.name = title; |
| 61 | +}; |
| 62 | + |
| 63 | +const isHash = (entity) => |
| 64 | + Boolean(entity && typeof entity === "object" && !Array.isArray(entity)); |
| 65 | +const nestedKeyValue = (hash, keys) => |
| 66 | + keys.reduce((hash, key) => (isHash(hash) ? hash[key] : undefined), hash); |
| 67 | +const isUndefined = (val) => val === undefined || val === null || val === ""; |
| 68 | +const evaluateSessionStatus = (status) => { |
| 69 | + if (!isUndefined(status)) { |
| 70 | + status = status.toLowerCase(); |
| 71 | + } |
| 72 | + if (status === "passed") { |
| 73 | + return "passed"; |
| 74 | + } else if (status === "failed" || status === "timedout") { |
| 75 | + return "failed"; |
| 76 | + } else { |
| 77 | + return ""; |
| 78 | + } |
| 79 | +}; |
| 80 | + |
| 81 | +exports.test = base.test.extend({ |
| 82 | + page: async ({ page, playwright }, use, testInfo) => { |
| 83 | + if (testInfo.project.name.match(/browserstack/)) { |
| 84 | + let vBrowser, vContext, vDevice; |
| 85 | + const isMobile = testInfo.project.name.match(/browserstack-mobile/); |
| 86 | + if (isMobile) { |
| 87 | + patchMobileCaps( |
| 88 | + testInfo.project.name, |
| 89 | + `${testInfo.file} - ${testInfo.title}` |
| 90 | + ); |
| 91 | + vDevice = await playwright._android.connect( |
| 92 | + `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent( |
| 93 | + JSON.stringify(caps) |
| 94 | + )}` |
| 95 | + ); |
| 96 | + await vDevice.shell("am force-stop com.android.chrome"); |
| 97 | + vContext = await vDevice.launchBrowser(); |
| 98 | + } else { |
| 99 | + patchCaps(testInfo.project.name, `${testInfo.title}`); |
| 100 | + delete caps.osVersion; |
| 101 | + delete caps.deviceName; |
| 102 | + delete caps.realMobile; |
| 103 | + vBrowser = await playwright.chromium.connect({ |
| 104 | + wsEndpoint: |
| 105 | + `wss://cdp.browserstack.com/playwright?caps=` + |
| 106 | + `${encodeURIComponent(JSON.stringify(caps))}`, |
| 107 | + }); |
| 108 | + vContext = await vBrowser.newContext(testInfo.project.use); |
| 109 | + } |
| 110 | + const vPage = await vContext.newPage(); |
| 111 | + await use(vPage); |
| 112 | + |
| 113 | + await vPage.close(); |
| 114 | + |
| 115 | + if (isMobile) { |
| 116 | + await vDevice.close(); |
| 117 | + } else { |
| 118 | + await vBrowser.close(); |
| 119 | + } |
| 120 | + } else { |
| 121 | + use(page); |
| 122 | + } |
| 123 | + }, |
| 124 | + |
| 125 | + beforeEach: [ |
| 126 | + async ({ page }, use) => { |
| 127 | + await page |
| 128 | + .context() |
| 129 | + .tracing.start({ screenshots: true, snapshots: true, sources: true }); |
| 130 | + await use(); |
| 131 | + }, |
| 132 | + { auto: true }, |
| 133 | + ], |
| 134 | + |
| 135 | + afterEach: [ |
| 136 | + async ({ page }, use, testInfo) => { |
| 137 | + await use(); |
| 138 | + if (testInfo.status == "failed") { |
| 139 | + await page |
| 140 | + .context() |
| 141 | + .tracing.stop({ path: `${testInfo.outputDir}/trace.zip` }); |
| 142 | + await page.screenshot({ path: `${testInfo.outputDir}/screenshot.png` }); |
| 143 | + await testInfo.attach("screenshot", { |
| 144 | + path: `${testInfo.outputDir}/screenshot.png`, |
| 145 | + contentType: "image/png", |
| 146 | + }); |
| 147 | + await testInfo.attach("trace", { |
| 148 | + path: `${testInfo.outputDir}/trace.zip`, |
| 149 | + contentType: "application/zip", |
| 150 | + }); |
| 151 | + } |
| 152 | + }, |
| 153 | + { auto: true }, |
| 154 | + ], |
| 155 | +}); |
0 commit comments