generated from saucelabs/new-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement minimum required plugin functions
- Loading branch information
1 parent
1c48897
commit d946391
Showing
8 changed files
with
204 additions
and
67 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import WebDriver, { Client } from 'webdriver'; | ||
|
||
export class SauceDriver { | ||
private readonly username: string; | ||
private readonly accessKey: string; | ||
private readonly tunnelName: string; | ||
private sessions = new Map<string, Client>(); | ||
|
||
constructor(username: string, accessKey: string, tunnelName: string) { | ||
this.username = username; | ||
this.accessKey = accessKey; | ||
this.tunnelName = tunnelName; | ||
} | ||
|
||
async openBrowser(url: string, browserName: string) { | ||
const webDriver = await WebDriver.newSession({ | ||
protocol: 'https', | ||
hostname: `ondemand.saucelabs.com`, // TODO multi region support | ||
port: 443, | ||
user: this.username, | ||
key: this.accessKey, | ||
capabilities: { | ||
name: 'testcafe sauce provider job', // TODO make this configurable | ||
browserName: browserName, | ||
buildName: 'TCPRVDR', // TODO make this configurable | ||
tunnelIdentifier: this.tunnelName, | ||
idleTimeout: 3600, // 1 hour | ||
}, | ||
logLevel: 'error', | ||
connectionRetryTimeout: 9 * 60 * 1000, // 9 minutes | ||
connectionRetryCount: 3, | ||
path: '/wd/hub', | ||
}); | ||
this.sessions.set(browserName, webDriver); | ||
|
||
// TODO do we need a keep-alive? | ||
|
||
await webDriver.navigateTo(url); | ||
|
||
return { | ||
jobUrl: `https://app.saucelabs.com/tests/${webDriver.sessionId}`, | ||
webDriver: webDriver, | ||
}; | ||
} | ||
|
||
async closeBrowser(browserId: string) { | ||
await this.sessions.get(browserId)?.deleteSession(); | ||
this.sessions.delete(browserId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export class AuthError extends Error { | ||
constructor() { | ||
super( | ||
'Authentication failed. Please assign the correct username and access key ' + | ||
'to the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables.', | ||
); | ||
this.name = 'AuthError'; | ||
} | ||
} | ||
|
||
export class TunnelNameError extends Error { | ||
constructor() { | ||
super( | ||
'The SAUCE_TUNNEL_NAME environment variable is not set. Please start a ' + | ||
'tunnel first and set the SAUCE_TUNNEL_NAME environment variable.', | ||
); | ||
this.name = 'TunnelNameError'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,128 @@ | ||
export default { | ||
import { SauceDriver } from './driver.js'; | ||
import { AuthError, TunnelNameError } from './errors'; | ||
|
||
let sauceDriver: SauceDriver; | ||
|
||
module.exports = { | ||
/** | ||
* Indicates whether the browser provider supports multiple browsers. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/index.ts#L65 | ||
*/ | ||
isMultiBrowser: true, | ||
|
||
// Required - must be implemented | ||
// Browser control | ||
async openBrowser(/* id, pageUrl, browserName */) { | ||
throw new Error('Not implemented!'); | ||
}, | ||
/** | ||
* Called by TestCafe to initialize the browser provider. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L81 | ||
*/ | ||
async init(): Promise<void> { | ||
const username = process.env.SAUCE_USERNAME; | ||
const accessKey = process.env.SAUCE_ACCESS_KEY; | ||
const tunnelName = process.env.SAUCE_TUNNEL_NAME; | ||
|
||
if (!username || !accessKey) { | ||
throw new AuthError(); | ||
} | ||
if (!tunnelName) { | ||
throw new TunnelNameError(); | ||
} | ||
|
||
async closeBrowser(/* id */) { | ||
throw new Error('Not implemented!'); | ||
sauceDriver = new SauceDriver(username, accessKey, tunnelName); | ||
}, | ||
|
||
// Optional - implement methods you need, remove other methods | ||
// Initialization | ||
async init() { | ||
return; | ||
/** | ||
* Called by TestCafe to open a browser. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L72 | ||
* | ||
* @param browserId | ||
* @param url | ||
* @param browserName | ||
*/ | ||
async openBrowser( | ||
browserId: string, | ||
url: string, | ||
browserName: string, | ||
): Promise<void> { | ||
// TODO check available concurrency and wait if necessary | ||
|
||
// TODO check tunnel status and wait if necessary | ||
// See https://docs.saucelabs.com/secure-connections/sauce-connect-5/operation/readiness-checks/. | ||
|
||
console.log('Starting browser on SauceDriver Labs...'); | ||
const { jobUrl } = await sauceDriver.openBrowser(url, browserName); | ||
console.log('Browser started.'); | ||
|
||
// Pass the job URL to TestCafe, which it will append to the test report. | ||
// Output: | ||
// Running tests in: | ||
// - Chrome 122.0.0.0 / Windows 10 (https://app.saucelabs.com/tests/8545f0fb12a24da290af1f6b87dcc530) | ||
this.setUserAgentMetaInfo(browserId, jobUrl); | ||
}, | ||
|
||
async dispose() { | ||
return; | ||
/** | ||
* Called by TestCafe to close a browser. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L76 | ||
* | ||
* @param browserId | ||
*/ | ||
async closeBrowser(browserId: string): Promise<void> { | ||
await sauceDriver.closeBrowser(browserId); | ||
}, | ||
|
||
// Browser names handling | ||
async getBrowserList() { | ||
throw new Error('Not implemented!'); | ||
/** | ||
* Called by TestCafe at the end of the test run. | ||
* Performs any cleanups necessary. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L85 | ||
*/ | ||
async dispose(): Promise<void> {}, | ||
|
||
/** | ||
* Called by TestCafe to get the list of available browsers. | ||
* | ||
* E.g. `"testcafe -b sauceDriver"` will call this method to print the available | ||
* browsers. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L91 | ||
*/ | ||
async getBrowserList(): Promise<string[]> { | ||
return ['chrome']; | ||
}, | ||
|
||
async isValidBrowserName(/* browserName */) { | ||
return true; | ||
/** | ||
* Called by TestCafe to verify if the user specified browser is valid. | ||
* | ||
* E.g. `"testcafe -b sauceDriver:chrome:latest"` will call this method to verify. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L95 | ||
* @param browserName | ||
*/ | ||
async isValidBrowserName(browserName: string): Promise<boolean> { | ||
return (await this.getBrowserList()).includes(browserName); | ||
}, | ||
|
||
// Extra methods | ||
/** | ||
* Called by TestCafe to resize the browser window. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/master/src/browser/provider/plugin-host.js#L126 | ||
*/ | ||
async resizeWindow(/* id, width, height, currentWidth, currentHeight */) { | ||
// this.reportWarning( | ||
// 'The window resize functionality is not supported by the "saucelabs-official" browser provider.', | ||
// ); | ||
this.reportWarning( | ||
'The window resize functionality is not supported by the Sauce Labs browser provider plugin.', | ||
); | ||
}, | ||
|
||
/** | ||
* Called by TestCafe to take a screenshot. | ||
* | ||
* https://github.com/DevExpress/testcafe/blob/4a30f1c3b8769ca68c9b7912911f1dd8aa91d62c/src/browser/provider/plugin-host.js#L134 | ||
*/ | ||
async takeScreenshot(/* id, screenshotPath, pageWidth, pageHeight */) { | ||
// this.reportWarning( | ||
// 'The screenshot functionality is not supported by the "saucelabs-official" browser provider.', | ||
// ); | ||
this.reportWarning( | ||
'The screenshot functionality is not supported by the Sauce Labs browser provider plugin.', | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters