feat: accessibility (#64)

This commit is contained in:
Yury Semikhatsky 2020-11-01 14:29:24 -08:00 committed by GitHub
parent d539159907
commit a592c12649
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 427 additions and 3 deletions

View File

@ -182,6 +182,7 @@ class Types {
add("Dialog.type", "string", "Type", new Empty()); add("Dialog.type", "string", "Type", new Empty());
add("ConsoleMessage.location", "Object", "Location"); add("ConsoleMessage.location", "Object", "Location");
add("ElementHandle.boundingBox", "Promise<null|Object>", "BoundingBox", new Empty()); add("ElementHandle.boundingBox", "Promise<null|Object>", "BoundingBox", new Empty());
add("Accessibility.snapshot", "Promise<null|Object>", "AccessibilityNode", new Empty());
add("Page.waitForRequest", "Promise<Request>", "Deferred<Request>"); add("Page.waitForRequest", "Promise<Request>", "Deferred<Request>");
add("Page.waitForResponse", "Promise<Response>", "Deferred<Response>"); add("Page.waitForResponse", "Promise<Response>", "Deferred<Response>");

View File

@ -32,9 +32,9 @@ public interface Accessibility {
return this; return this;
} }
} }
default Object snapshot() { default AccessibilityNode snapshot() {
return snapshot(null); return snapshot(null);
} }
Object snapshot(SnapshotOptions options); AccessibilityNode snapshot(SnapshotOptions options);
} }

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed 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 com.microsoft.playwright;
import java.util.List;
public interface AccessibilityNode {
String role();
String name();
String valueString();
Double valueNumber();
String description();
String keyshortcuts();
String roledescription();
String valuetext();
Boolean disabled();
Boolean expanded();
Boolean focused();
Boolean modal();
Boolean multiline();
Boolean multiselectable();
Boolean readonly();
Boolean required();
Boolean selected();
enum CheckedState { CHECKED, UNCHECKED, MIXED }
CheckedState checked();
enum PressedState { PRESSED, RELEASED, MIXED }
PressedState pressed();
Integer level();
Double valuemin();
Double valuemax();
String autocomplete();
String haspopup();
String invalid();
String orientation();
List<AccessibilityNode> children();
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed 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 com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Accessibility;
import com.microsoft.playwright.AccessibilityNode;
import static com.microsoft.playwright.impl.Serialization.gson;
class AccessibilityImpl implements Accessibility {
private final PageImpl page;
AccessibilityImpl(PageImpl page) {
this.page = page;
}
@Override
public AccessibilityNode snapshot(SnapshotOptions options) {
if (options == null) {
options = new SnapshotOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
JsonObject json = page.sendMessage("accessibilitySnapshot", params).getAsJsonObject();
if (!json.has("rootAXNode")) {
return null;
}
return new AccessibilityNodeImpl(json.getAsJsonObject("rootAXNode"));
}
}

View File

@ -0,0 +1,258 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed 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 com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.AccessibilityNode;
import java.util.ArrayList;
import java.util.List;
class AccessibilityNodeImpl implements AccessibilityNode {
private final JsonObject json;
AccessibilityNodeImpl(JsonObject json) {
this.json = json;
}
@Override
public String role() {
return json.get("role").getAsString();
}
@Override
public String name() {
return json.get("name").getAsString();
}
@Override
public String valueString() {
if (!json.has("valueString")) {
return null;
}
return json.get("valueString").getAsString();
}
@Override
public Double valueNumber() {
if (!json.has("valueNumber")) {
return null;
}
return json.get("valueNumber").getAsDouble();
}
@Override
public String description() {
if (!json.has("description")) {
return null;
}
return json.get("description").getAsString();
}
@Override
public String keyshortcuts() {
if (!json.has("keyshortcuts")) {
return null;
}
return json.get("keyshortcuts").getAsString();
}
@Override
public String roledescription() {
if (!json.has("roledescription")) {
return null;
}
return json.get("roledescription").getAsString();
}
@Override
public String valuetext() {
if (!json.has("valuetext")) {
return null;
}
return json.get("valuetext").getAsString();
}
@Override
public Boolean disabled() {
if (!json.has("disabled")) {
return null;
}
return json.get("disabled").getAsBoolean();
}
@Override
public Boolean expanded() {
if (!json.has("expanded")) {
return null;
}
return json.get("expanded").getAsBoolean();
}
@Override
public Boolean focused() {
if (!json.has("focused")) {
return null;
}
return json.get("focused").getAsBoolean();
}
@Override
public Boolean modal() {
if (!json.has("modal")) {
return null;
}
return json.get("modal").getAsBoolean();
}
@Override
public Boolean multiline() {
if (!json.has("multiline")) {
return null;
}
return json.get("multiline").getAsBoolean();
}
@Override
public Boolean multiselectable() {
if (!json.has("multiselectable")) {
return null;
}
return json.get("multiselectable").getAsBoolean();
}
@Override
public Boolean readonly() {
if (!json.has("readonly")) {
return null;
}
return json.get("readonly").getAsBoolean();
}
@Override
public Boolean required() {
if (!json.has("required")) {
return null;
}
return json.get("required").getAsBoolean();
}
@Override
public Boolean selected() {
if (!json.has("selected")) {
return null;
}
return json.get("selected").getAsBoolean();
}
@Override
public CheckedState checked() {
if (!json.has("checked")) {
return null;
}
String value = json.get("checked").getAsString();
switch (value) {
case "checked": return CheckedState.CHECKED;
case "unchecked": return CheckedState.UNCHECKED;
case "mixed": return CheckedState.MIXED;
default: throw new IllegalStateException("Unexpected value: " + value);
}
}
@Override
public PressedState pressed() {
if (!json.has("pressed")) {
return null;
}
String value = json.get("pressed").getAsString();
switch (value) {
case "pressed": return PressedState.PRESSED;
case "released": return PressedState.RELEASED;
case "mixed": return PressedState.MIXED;
default: throw new IllegalStateException("Unexpected value: " + value);
}
}
@Override
public Integer level() {
if (!json.has("level")) {
return null;
}
return json.get("level").getAsInt();
}
@Override
public Double valuemin() {
if (!json.has("valuemin")) {
return null;
}
return json.get("valuemin").getAsDouble();
}
@Override
public Double valuemax() {
if (!json.has("valuemax")) {
return null;
}
return json.get("valuemax").getAsDouble();
}
@Override
public String autocomplete() {
if (!json.has("autocomplete")) {
return null;
}
return json.get("autocomplete").getAsString();
}
@Override
public String haspopup() {
if (!json.has("haspopup")) {
return null;
}
return json.get("haspopup").getAsString();
}
@Override
public String invalid() {
if (!json.has("invalid")) {
return null;
}
return json.get("invalid").getAsString();
}
@Override
public String orientation() {
if (!json.has("orientation")) {
return null;
}
return json.get("orientation").getAsString();
}
@Override
public List<AccessibilityNode> children() {
if (!json.has("children")) {
return null;
}
List<AccessibilityNode> result = new ArrayList<>();
for (JsonElement e : json.getAsJsonArray("children")) {
result.add(new AccessibilityNodeImpl(e.getAsJsonObject()));
}
return result;
}
}

View File

@ -37,6 +37,7 @@ public class PageImpl extends ChannelOwner implements Page {
private final FrameImpl mainFrame; private final FrameImpl mainFrame;
private final KeyboardImpl keyboard; private final KeyboardImpl keyboard;
private final MouseImpl mouse; private final MouseImpl mouse;
private final AccessibilityImpl accessibility;
private Viewport viewport; private Viewport viewport;
private final Router routes = new Router(); private final Router routes = new Router();
private final Set<FrameImpl> frames = new LinkedHashSet<>(); private final Set<FrameImpl> frames = new LinkedHashSet<>();
@ -54,6 +55,7 @@ public class PageImpl extends ChannelOwner implements Page {
mainFrame.page = this; mainFrame.page = this;
keyboard = new KeyboardImpl(this); keyboard = new KeyboardImpl(this);
mouse = new MouseImpl(this); mouse = new MouseImpl(this);
accessibility = new AccessibilityImpl(this);
frames.add(mainFrame); frames.add(mainFrame);
timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings); timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings);
} }
@ -232,7 +234,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override @Override
public Accessibility accessibility() { public Accessibility accessibility() {
return null; return accessibility;
} }
@Override @Override

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed 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 com.microsoft.playwright;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestAccessibility extends TestBase {
@Test
void shouldWorkWithRegularText() {
page.setContent("<div>Hello World</div>");
AccessibilityNode snapshot = page.accessibility().snapshot();
AccessibilityNode node = snapshot.children().get(0);
assertEquals(isFirefox ? "text leaf" : "text", node.role());
assertEquals("Hello World", node.name());
}
@Test
void roledescription() {
page.setContent("<div tabIndex=-1 aria-roledescription='foo'>Hi</div>");
AccessibilityNode snapshot = page.accessibility().snapshot();
assertEquals("foo", snapshot.children().get(0).roledescription());
}
@Test
void orientation() {
page.setContent("<a href='' role='slider' aria-orientation='vertical'>11</a>");
AccessibilityNode snapshot = page.accessibility().snapshot();
assertEquals("vertical", snapshot.children().get(0).orientation());
}
@Test
void autocomplete() {
page.setContent("<div role='textbox' aria-autocomplete='list'>hi</div>");
AccessibilityNode snapshot = page.accessibility().snapshot();
assertEquals("list", snapshot.children().get(0).autocomplete());
}
@Test
void multiselectable() {
page.setContent("<div role='grid' tabIndex=-1 aria-multiselectable=true>hey</div>");
AccessibilityNode snapshot = page.accessibility().snapshot();
assertEquals(true, snapshot.children().get(0).multiselectable());
}
@Test
void keyshortcuts() {
page.setContent("<div role='grid' tabIndex=-1 aria-keyshortcuts='foo'>hey</div>");
AccessibilityNode snapshot = page.accessibility().snapshot();
assertEquals("foo", snapshot.children().get(0).keyshortcuts());
}
}