Skip to content

Commit

Permalink
[bidi][java][js] Add setFiles command of the Input Module (#13711)
Browse files Browse the repository at this point in the history
  • Loading branch information
pujagani authored Mar 20, 2024
1 parent e195d79 commit ceaa738
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 7 deletions.
30 changes: 30 additions & 0 deletions java/src/org/openqa/selenium/bidi/module/Input.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -27,6 +28,7 @@
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.Command;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.bidi.script.RemoteReference;
import org.openqa.selenium.interactions.Sequence;

public class Input {
Expand Down Expand Up @@ -87,4 +89,32 @@ public void perform(String browsingContext, Collection<Sequence> actions) {
public void release(String browsingContext) {
bidi.send(new Command<>("input.releaseActions", Map.of("context", browsingContext)));
}

public void setFiles(String browsingContext, RemoteReference element, List<String> files) {
bidi.send(
new Command<>(
"input.setFiles",
Map.of("context", browsingContext, "element", element.toJson(), "files", files)));
}

public void setFiles(String browsingContext, String elementId, List<String> files) {
bidi.send(
new Command<>(
"input.setFiles",
Map.of(
"context",
browsingContext,
"element",
new RemoteReference(RemoteReference.Type.SHARED_ID, elementId).toJson(),
"files",
files)));
}

public void setFiles(String browsingContext, RemoteReference element, String file) {
setFiles(browsingContext, element, Collections.singletonList(file));
}

public void setFiles(String browsingContext, String elementId, String file) {
setFiles(browsingContext, elementId, Collections.singletonList(file));
}
}
143 changes: 143 additions & 0 deletions java/test/org/openqa/selenium/bidi/input/SetFilesCommandTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.

package org.openqa.selenium.bidi.input;

import static org.assertj.core.api.Assertions.assertThat;
import static org.openqa.selenium.testing.drivers.Browser.EDGE;
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
import static org.openqa.selenium.testing.drivers.Browser.IE;
import static org.openqa.selenium.testing.drivers.Browser.SAFARI;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.bidi.module.Input;
import org.openqa.selenium.bidi.script.RemoteReference;
import org.openqa.selenium.environment.webserver.AppServer;
import org.openqa.selenium.environment.webserver.NettyAppServer;
import org.openqa.selenium.remote.RemoteWebElement;
import org.openqa.selenium.testing.JupiterTestBase;
import org.openqa.selenium.testing.NotYetImplemented;

public class SetFilesCommandTest extends JupiterTestBase {
private Input input;

private String windowHandle;

private AppServer server;

@BeforeEach
public void setUp() {
windowHandle = driver.getWindowHandle();
input = new Input(driver);
server = new NettyAppServer();
server.start();
}

@Test
@NotYetImplemented(SAFARI)
@NotYetImplemented(IE)
@NotYetImplemented(EDGE)
@NotYetImplemented(FIREFOX)
void canSetFiles() throws IOException {
driver.get(pages.formPage);
WebElement uploadElement = driver.findElement(By.id("upload"));
assertThat(uploadElement.getAttribute("value")).isEmpty();

File file = File.createTempFile("test", "txt");
file.deleteOnExit();

List<String> paths = new ArrayList<>();
paths.add(file.getAbsolutePath());

input.setFiles(
windowHandle,
new RemoteReference(
RemoteReference.Type.SHARED_ID, ((RemoteWebElement) uploadElement).getId()),
paths);

assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
}

@Test
@NotYetImplemented(SAFARI)
@NotYetImplemented(IE)
@NotYetImplemented(EDGE)
@NotYetImplemented(FIREFOX)
public void canSetFilesWithElementId() throws IOException {
driver.get(pages.formPage);
WebElement uploadElement = driver.findElement(By.id("upload"));
assertThat(uploadElement.getAttribute("value")).isEmpty();

File file = File.createTempFile("test", "txt");
file.deleteOnExit();

List<String> paths = new ArrayList<>();
paths.add(file.getAbsolutePath());

input.setFiles(windowHandle, ((RemoteWebElement) uploadElement).getId(), paths);

assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
}

@Test
@NotYetImplemented(SAFARI)
@NotYetImplemented(IE)
@NotYetImplemented(EDGE)
@NotYetImplemented(FIREFOX)
void canSetFile() throws IOException {
driver.get(pages.formPage);
WebElement uploadElement = driver.findElement(By.id("upload"));
assertThat(uploadElement.getAttribute("value")).isEmpty();

File file = File.createTempFile("test", "txt");
file.deleteOnExit();

input.setFiles(
windowHandle,
new RemoteReference(
RemoteReference.Type.SHARED_ID, ((RemoteWebElement) uploadElement).getId()),
file.getAbsolutePath());

assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
}

@Test
@NotYetImplemented(SAFARI)
@NotYetImplemented(IE)
@NotYetImplemented(EDGE)
@NotYetImplemented(FIREFOX)
void canSetFileWithElementId() throws IOException {
driver.get(pages.formPage);
WebElement uploadElement = driver.findElement(By.id("upload"));
assertThat(uploadElement.getAttribute("value")).isEmpty();

File file = File.createTempFile("test", "txt");
file.deleteOnExit();

input.setFiles(
windowHandle, ((RemoteWebElement) uploadElement).getId(), file.getAbsolutePath());

assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
}
}
21 changes: 21 additions & 0 deletions javascript/node/selenium-webdriver/bidi/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// type: module added to package.json
// import { WebElement } from '../lib/webdriver'
const { WebElement } = require('../lib/webdriver')
const { RemoteReferenceType, ReferenceValue } = require('./protocolValue')

class Input {
constructor(driver) {
Expand Down Expand Up @@ -57,6 +58,26 @@ class Input {
}
return await this.bidi.send(command)
}

async setFiles(browsingContextId, element, files) {

if (typeof element !== 'string' && !(element instanceof ReferenceValue)) {
throw Error(`Pass in a WebElement id as a string or a ReferenceValue. Received: ${element}`)
}

const command = {
method: 'input.setFiles',
params: {
context: browsingContextId,
element:
typeof element === 'string'
? new ReferenceValue(RemoteReferenceType.SHARED_ID, element).asMap()
: element.asMap(),
files: typeof files === 'string' ? [files] : files,
},
}
await this.bidi.send(command)
}
}

async function updateActions(actions) {
Expand Down
18 changes: 11 additions & 7 deletions javascript/node/selenium-webdriver/bidi/protocolValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,23 +160,27 @@ class RemoteValue {
}

class ReferenceValue {
#handle
#sharedId
constructor(handle, sharedId) {
if (handle === RemoteReferenceType.HANDLE) {
this.handle = sharedId
this.#handle = sharedId
} else if (handle === RemoteReferenceType.SHARED_ID) {
this.#sharedId = sharedId
} else {
this.handle = handle
this.sharedId = sharedId
this.#handle = handle
this.#sharedId = sharedId
}
}

asMap() {
const toReturn = {}
if (this.handle != null) {
toReturn[RemoteReferenceType.HANDLE] = this.handle
if (this.#handle != null) {
toReturn[RemoteReferenceType.HANDLE] = this.#handle
}

if (this.sharedId != null) {
toReturn[RemoteReferenceType.SHARED_ID] = this.sharedId
if (this.#sharedId != null) {
toReturn[RemoteReferenceType.SHARED_ID] = this.#sharedId
}

return toReturn
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.

'use strict'

const assert = require('assert')
require('../../lib/test/fileserver')
const firefox = require('../../firefox')
const { Pages, suite } = require('../../lib/test')
const { Browser, By } = require('../..')
const Input = require('../../bidi/input')
const io = require('../../io')
const fs = require('node:fs')
const { ReferenceValue, RemoteReferenceType } = require('../../bidi/protocolValue')

suite(
function (env) {
describe('Input Set Files', function () {
const FILE_HTML = '<!DOCTYPE html><div>' + 'Hello' + '</div>'
let driver

let _fp
before(function () {
return (_fp = io.tmpFile().then(function (fp) {
fs.writeFileSync(fp, FILE_HTML)
return fp
}))
})

beforeEach(async function () {
driver = await env.builder().setFirefoxOptions(new firefox.Options().enableBidi()).build()
})

afterEach(function () {
return driver.quit()
})

xit('can set files', async function () {
const browsingContextId = await driver.getWindowHandle()
const input = await Input(driver)
await driver.get(Pages.formPage)

const filePath = await io.tmpFile().then(function(fp) {
fs.writeFileSync(fp, FILE_HTML)
return fp
})

const webElement = await driver.findElement(By.id('upload'))

assert.strictEqual(await webElement.getAttribute('value'), '')

const webElementId = await webElement.getId()

await input.setFiles(browsingContextId, new ReferenceValue(RemoteReferenceType.SHARED_ID, webElementId), [filePath])

assert.notEqual(await webElement.getAttribute('value'), '')
})

xit('can set files with element id', async function () {
const browsingContextId = await driver.getWindowHandle()
const input = await Input(driver)
await driver.get(Pages.formPage)

const filePath = await io.tmpFile().then(function(fp) {
fs.writeFileSync(fp, FILE_HTML)
return fp
})

const webElement = await driver.findElement(By.id('upload'))

assert.strictEqual(await webElement.getAttribute('value'), '')

const webElementId = await webElement.getId()

await input.setFiles(browsingContextId, webElementId, filePath)

assert.notEqual(await webElement.getAttribute('value'), '')
})
})
},
{ browsers: [Browser.FIREFOX] },
)

0 comments on commit ceaa738

Please sign in to comment.