Skip to content

Commit ceaa738

Browse files
authored
[bidi][java][js] Add setFiles command of the Input Module (#13711)
1 parent e195d79 commit ceaa738

File tree

5 files changed

+301
-7
lines changed

5 files changed

+301
-7
lines changed

java/src/org/openqa/selenium/bidi/module/Input.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.lang.reflect.InvocationTargetException;
2121
import java.util.Collection;
22+
import java.util.Collections;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.stream.Collectors;
@@ -27,6 +28,7 @@
2728
import org.openqa.selenium.bidi.BiDi;
2829
import org.openqa.selenium.bidi.Command;
2930
import org.openqa.selenium.bidi.HasBiDi;
31+
import org.openqa.selenium.bidi.script.RemoteReference;
3032
import org.openqa.selenium.interactions.Sequence;
3133

3234
public class Input {
@@ -87,4 +89,32 @@ public void perform(String browsingContext, Collection<Sequence> actions) {
8789
public void release(String browsingContext) {
8890
bidi.send(new Command<>("input.releaseActions", Map.of("context", browsingContext)));
8991
}
92+
93+
public void setFiles(String browsingContext, RemoteReference element, List<String> files) {
94+
bidi.send(
95+
new Command<>(
96+
"input.setFiles",
97+
Map.of("context", browsingContext, "element", element.toJson(), "files", files)));
98+
}
99+
100+
public void setFiles(String browsingContext, String elementId, List<String> files) {
101+
bidi.send(
102+
new Command<>(
103+
"input.setFiles",
104+
Map.of(
105+
"context",
106+
browsingContext,
107+
"element",
108+
new RemoteReference(RemoteReference.Type.SHARED_ID, elementId).toJson(),
109+
"files",
110+
files)));
111+
}
112+
113+
public void setFiles(String browsingContext, RemoteReference element, String file) {
114+
setFiles(browsingContext, element, Collections.singletonList(file));
115+
}
116+
117+
public void setFiles(String browsingContext, String elementId, String file) {
118+
setFiles(browsingContext, elementId, Collections.singletonList(file));
119+
}
90120
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.bidi.input;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.openqa.selenium.testing.drivers.Browser.EDGE;
22+
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
23+
import static org.openqa.selenium.testing.drivers.Browser.IE;
24+
import static org.openqa.selenium.testing.drivers.Browser.SAFARI;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import org.junit.jupiter.api.BeforeEach;
31+
import org.junit.jupiter.api.Test;
32+
import org.openqa.selenium.By;
33+
import org.openqa.selenium.WebElement;
34+
import org.openqa.selenium.bidi.module.Input;
35+
import org.openqa.selenium.bidi.script.RemoteReference;
36+
import org.openqa.selenium.environment.webserver.AppServer;
37+
import org.openqa.selenium.environment.webserver.NettyAppServer;
38+
import org.openqa.selenium.remote.RemoteWebElement;
39+
import org.openqa.selenium.testing.JupiterTestBase;
40+
import org.openqa.selenium.testing.NotYetImplemented;
41+
42+
public class SetFilesCommandTest extends JupiterTestBase {
43+
private Input input;
44+
45+
private String windowHandle;
46+
47+
private AppServer server;
48+
49+
@BeforeEach
50+
public void setUp() {
51+
windowHandle = driver.getWindowHandle();
52+
input = new Input(driver);
53+
server = new NettyAppServer();
54+
server.start();
55+
}
56+
57+
@Test
58+
@NotYetImplemented(SAFARI)
59+
@NotYetImplemented(IE)
60+
@NotYetImplemented(EDGE)
61+
@NotYetImplemented(FIREFOX)
62+
void canSetFiles() throws IOException {
63+
driver.get(pages.formPage);
64+
WebElement uploadElement = driver.findElement(By.id("upload"));
65+
assertThat(uploadElement.getAttribute("value")).isEmpty();
66+
67+
File file = File.createTempFile("test", "txt");
68+
file.deleteOnExit();
69+
70+
List<String> paths = new ArrayList<>();
71+
paths.add(file.getAbsolutePath());
72+
73+
input.setFiles(
74+
windowHandle,
75+
new RemoteReference(
76+
RemoteReference.Type.SHARED_ID, ((RemoteWebElement) uploadElement).getId()),
77+
paths);
78+
79+
assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
80+
}
81+
82+
@Test
83+
@NotYetImplemented(SAFARI)
84+
@NotYetImplemented(IE)
85+
@NotYetImplemented(EDGE)
86+
@NotYetImplemented(FIREFOX)
87+
public void canSetFilesWithElementId() throws IOException {
88+
driver.get(pages.formPage);
89+
WebElement uploadElement = driver.findElement(By.id("upload"));
90+
assertThat(uploadElement.getAttribute("value")).isEmpty();
91+
92+
File file = File.createTempFile("test", "txt");
93+
file.deleteOnExit();
94+
95+
List<String> paths = new ArrayList<>();
96+
paths.add(file.getAbsolutePath());
97+
98+
input.setFiles(windowHandle, ((RemoteWebElement) uploadElement).getId(), paths);
99+
100+
assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
101+
}
102+
103+
@Test
104+
@NotYetImplemented(SAFARI)
105+
@NotYetImplemented(IE)
106+
@NotYetImplemented(EDGE)
107+
@NotYetImplemented(FIREFOX)
108+
void canSetFile() throws IOException {
109+
driver.get(pages.formPage);
110+
WebElement uploadElement = driver.findElement(By.id("upload"));
111+
assertThat(uploadElement.getAttribute("value")).isEmpty();
112+
113+
File file = File.createTempFile("test", "txt");
114+
file.deleteOnExit();
115+
116+
input.setFiles(
117+
windowHandle,
118+
new RemoteReference(
119+
RemoteReference.Type.SHARED_ID, ((RemoteWebElement) uploadElement).getId()),
120+
file.getAbsolutePath());
121+
122+
assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
123+
}
124+
125+
@Test
126+
@NotYetImplemented(SAFARI)
127+
@NotYetImplemented(IE)
128+
@NotYetImplemented(EDGE)
129+
@NotYetImplemented(FIREFOX)
130+
void canSetFileWithElementId() throws IOException {
131+
driver.get(pages.formPage);
132+
WebElement uploadElement = driver.findElement(By.id("upload"));
133+
assertThat(uploadElement.getAttribute("value")).isEmpty();
134+
135+
File file = File.createTempFile("test", "txt");
136+
file.deleteOnExit();
137+
138+
input.setFiles(
139+
windowHandle, ((RemoteWebElement) uploadElement).getId(), file.getAbsolutePath());
140+
141+
assertThat(uploadElement.getAttribute("value")).endsWith(file.getName());
142+
}
143+
}

javascript/node/selenium-webdriver/bidi/input.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// type: module added to package.json
1919
// import { WebElement } from '../lib/webdriver'
2020
const { WebElement } = require('../lib/webdriver')
21+
const { RemoteReferenceType, ReferenceValue } = require('./protocolValue')
2122

2223
class Input {
2324
constructor(driver) {
@@ -57,6 +58,26 @@ class Input {
5758
}
5859
return await this.bidi.send(command)
5960
}
61+
62+
async setFiles(browsingContextId, element, files) {
63+
64+
if (typeof element !== 'string' && !(element instanceof ReferenceValue)) {
65+
throw Error(`Pass in a WebElement id as a string or a ReferenceValue. Received: ${element}`)
66+
}
67+
68+
const command = {
69+
method: 'input.setFiles',
70+
params: {
71+
context: browsingContextId,
72+
element:
73+
typeof element === 'string'
74+
? new ReferenceValue(RemoteReferenceType.SHARED_ID, element).asMap()
75+
: element.asMap(),
76+
files: typeof files === 'string' ? [files] : files,
77+
},
78+
}
79+
await this.bidi.send(command)
80+
}
6081
}
6182

6283
async function updateActions(actions) {

javascript/node/selenium-webdriver/bidi/protocolValue.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,27 @@ class RemoteValue {
160160
}
161161

162162
class ReferenceValue {
163+
#handle
164+
#sharedId
163165
constructor(handle, sharedId) {
164166
if (handle === RemoteReferenceType.HANDLE) {
165-
this.handle = sharedId
167+
this.#handle = sharedId
168+
} else if (handle === RemoteReferenceType.SHARED_ID) {
169+
this.#sharedId = sharedId
166170
} else {
167-
this.handle = handle
168-
this.sharedId = sharedId
171+
this.#handle = handle
172+
this.#sharedId = sharedId
169173
}
170174
}
171175

172176
asMap() {
173177
const toReturn = {}
174-
if (this.handle != null) {
175-
toReturn[RemoteReferenceType.HANDLE] = this.handle
178+
if (this.#handle != null) {
179+
toReturn[RemoteReferenceType.HANDLE] = this.#handle
176180
}
177181

178-
if (this.sharedId != null) {
179-
toReturn[RemoteReferenceType.SHARED_ID] = this.sharedId
182+
if (this.#sharedId != null) {
183+
toReturn[RemoteReferenceType.SHARED_ID] = this.#sharedId
180184
}
181185

182186
return toReturn
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
'use strict'
19+
20+
const assert = require('assert')
21+
require('../../lib/test/fileserver')
22+
const firefox = require('../../firefox')
23+
const { Pages, suite } = require('../../lib/test')
24+
const { Browser, By } = require('../..')
25+
const Input = require('../../bidi/input')
26+
const io = require('../../io')
27+
const fs = require('node:fs')
28+
const { ReferenceValue, RemoteReferenceType } = require('../../bidi/protocolValue')
29+
30+
suite(
31+
function (env) {
32+
describe('Input Set Files', function () {
33+
const FILE_HTML = '<!DOCTYPE html><div>' + 'Hello' + '</div>'
34+
let driver
35+
36+
let _fp
37+
before(function () {
38+
return (_fp = io.tmpFile().then(function (fp) {
39+
fs.writeFileSync(fp, FILE_HTML)
40+
return fp
41+
}))
42+
})
43+
44+
beforeEach(async function () {
45+
driver = await env.builder().setFirefoxOptions(new firefox.Options().enableBidi()).build()
46+
})
47+
48+
afterEach(function () {
49+
return driver.quit()
50+
})
51+
52+
xit('can set files', async function () {
53+
const browsingContextId = await driver.getWindowHandle()
54+
const input = await Input(driver)
55+
await driver.get(Pages.formPage)
56+
57+
const filePath = await io.tmpFile().then(function(fp) {
58+
fs.writeFileSync(fp, FILE_HTML)
59+
return fp
60+
})
61+
62+
const webElement = await driver.findElement(By.id('upload'))
63+
64+
assert.strictEqual(await webElement.getAttribute('value'), '')
65+
66+
const webElementId = await webElement.getId()
67+
68+
await input.setFiles(browsingContextId, new ReferenceValue(RemoteReferenceType.SHARED_ID, webElementId), [filePath])
69+
70+
assert.notEqual(await webElement.getAttribute('value'), '')
71+
})
72+
73+
xit('can set files with element id', async function () {
74+
const browsingContextId = await driver.getWindowHandle()
75+
const input = await Input(driver)
76+
await driver.get(Pages.formPage)
77+
78+
const filePath = await io.tmpFile().then(function(fp) {
79+
fs.writeFileSync(fp, FILE_HTML)
80+
return fp
81+
})
82+
83+
const webElement = await driver.findElement(By.id('upload'))
84+
85+
assert.strictEqual(await webElement.getAttribute('value'), '')
86+
87+
const webElementId = await webElement.getId()
88+
89+
await input.setFiles(browsingContextId, webElementId, filePath)
90+
91+
assert.notEqual(await webElement.getAttribute('value'), '')
92+
})
93+
})
94+
},
95+
{ browsers: [Browser.FIREFOX] },
96+
)

0 commit comments

Comments
 (0)