Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions examples/javascript/test/drivers/gridServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const { spawn } = require('node:child_process')
const http = require('node:http')
const path = require('node:path')
const portprober = require('selenium-webdriver/net/portprober')

const SERVER_JAR = path.join(__dirname, '..', '..', '..', 'selenium-server-4.49.0.jar')

function waitForServer(url, timeout) {
const deadline = Date.now() + timeout

return new Promise((resolve, reject) => {
function retryOrFail() {
if (Date.now() > deadline) {
reject(new Error(`Selenium server did not start within ${timeout}ms`))
return
}
setTimeout(attempt, 200)
}

function attempt() {
let settled = false
const retryOnce = () => {
if (settled) return
settled = true
retryOrFail()
}

const req = http.get(url, { timeout: 2000 }, (res) => {
let body = ''
res.on('data', (chunk) => (body += chunk))
res.on('error', retryOnce)
res.on('aborted', retryOnce)
res.on('end', () => {
if (settled) return
try {
const { value } = JSON.parse(body)
if (res.statusCode === 200 && value && value.ready) {
settled = true
resolve()
return
}
} catch (err) {
// Not valid JSON yet (e.g. server still booting) - fall through to retry.
}
retryOnce()
})
})
req.on('timeout', () => {
req.destroy()
retryOnce()
})
req.on('error', retryOnce)
}
attempt()
})
}

async function startGrid() {
const port = await portprober.findFreePort()
const child = spawn('java', [
'-jar',
SERVER_JAR,
'standalone',
'--port',
String(port),
'--selenium-manager',
'true',
'--enable-managed-downloads',
'true',
'--log-level',
'WARNING',
])

const url = `http://localhost:${port}`
try {
await waitForServer(`${url}/status`, 60000)
} catch (err) {
child.kill()
throw err
}

return { url, process: child }
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
}

function stopGrid(child) {
if (child) {
child.kill()
}
}

module.exports = { startGrid, stopGrid }
96 changes: 96 additions & 0 deletions examples/javascript/test/drivers/remote_webdriver.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const { Browser, Builder, By } = require('selenium-webdriver')
const Chrome = require('selenium-webdriver/chrome')
const remote = require('selenium-webdriver/remote')
const assert = require('node:assert')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { startGrid, stopGrid } = require('./gridServer')

async function waitForFile(filePath, timeout) {
const deadline = Date.now() + timeout
while (!fs.existsSync(filePath)) {
if (Date.now() > deadline) {
throw new Error(`Timed out waiting for ${filePath} to be written`)
}
await new Promise((resolve) => setTimeout(resolve, 100))
}
}

describe('Remote WebDriver Test', function () {
let driver
let gridUrl
let gridProcess

beforeEach(async function () {
const grid = await startGrid()
gridUrl = grid.url
gridProcess = grid.process
})

afterEach(async function () {
try {
if (driver) {
await driver.quit()
driver = null
}
} finally {
stopGrid(gridProcess)
}
})

it('Basic Example', async function () {
const options = new Chrome.Options().addArguments('--no-sandbox')
driver = new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).usingServer(gridUrl).build()
})

it('Uploads', async function () {
const options = new Chrome.Options().addArguments('--no-sandbox')
driver = new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).usingServer(gridUrl).build()
await driver.get('https://the-internet.herokuapp.com/upload')
const uploadFile = path.resolve('./test/resources/selenium-snapshot.png')

await driver.setFileDetector(new remote.FileDetector())
await driver.findElement(By.css('input[type=file]')).sendKeys(uploadFile)
await driver.findElement(By.id('file-submit')).click()

const fileName = await driver.findElement(By.id('uploaded-files')).getText()
assert.strictEqual(fileName, 'selenium-snapshot.png')
})

it('Downloads', async function () {
const options = new Chrome.Options().addArguments('--no-sandbox').enableDownloads()
driver = new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).usingServer(gridUrl).build()

const fileNames = ['file_1.txt', 'file_2.jpg']
await driver.get('https://www.selenium.dev/selenium/web/downloads/download.html')
await driver.findElement(By.id('file-1')).click()
await driver.findElement(By.id('file-2')).click()
await driver.wait(async (d) => {
const downloaded = await d.getDownloadableFiles()
return fileNames.every((name) => downloaded.includes(name))
}, 5000)

const files = await driver.getDownloadableFiles()
assert.deepStrictEqual(files.sort(), fileNames.sort())

const targetDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'downloads-'))
const downloadableFile = files[0]
await driver.downloadFile(downloadableFile, targetDirectory)

const downloadedFilePath = path.join(targetDirectory, downloadableFile)
await waitForFile(downloadedFilePath, 5000)
const fileContent = fs.readFileSync(downloadedFilePath, 'utf8')
assert.strictEqual(fileContent.trim(), 'Hello, World!')

await driver.deleteDownloadableFiles()
assert.deepStrictEqual(await driver.getDownloadableFiles(), [])
})

it('Browser specific functionalities', async function () {
const options = new Chrome.Options().addArguments('--no-sandbox')
driver = new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).usingServer(gridUrl).build()

assert.ok(driver instanceof Chrome.Driver)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ and an options instance are both required.
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L20-L21" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L43-L44" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand Down Expand Up @@ -85,7 +85,8 @@ Ruby adds a local file detector to remote webdriver instances by default, but yo
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L33-L36" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
JavaScript does not include a Local File Detector by default, so you must always add one to do uploads.
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L53-L55" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand Down Expand Up @@ -126,7 +127,7 @@ Each of the bindings have a method in the options class to set this.
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L43-L44" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L62-L63" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand All @@ -152,7 +153,7 @@ so the list is an immediate snapshot of what file names are currently in the dir
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L52" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L74" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand All @@ -177,7 +178,7 @@ Selenium looks for the name of the provided file in the list and downloads it to
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L57" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L79" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand All @@ -203,7 +204,7 @@ but you can also delete all files during the session.
{{< gh-codeblock path="/examples/ruby/spec/drivers/remote_webdriver_spec.rb#L62" >}}
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L86" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand Down Expand Up @@ -239,7 +240,9 @@ Ruby uses mixins to add applicable browser specific methods to the Remote WebDri
the methods should always just work for you.
{{< /tab >}}
{{< tab header="JavaScript" >}}
{{< badge-code >}}
JavaScript returns the browser-specific driver class (such as `chrome.Driver`) directly when connecting to a
Remote Session, so no augmenting step is needed to access browser specific functionality.
{{< gh-codeblock path="/examples/javascript/test/drivers/remote_webdriver.spec.js#L91-L94" >}}
{{< /tab >}}
{{< tab header="Kotlin" >}}
{{< badge-code >}}
Expand Down
Loading