fix: delete deprecated accessibility API, add onceDialog (#317)
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
/**
|
||||
* The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by
|
||||
* assistive technology such as <a href="https://en.wikipedia.org/wiki/Screen_reader">screen readers</a> or <a
|
||||
* href="https://en.wikipedia.org/wiki/Switch_access">switches</a>.
|
||||
*
|
||||
* <p> Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might
|
||||
* have wildly different output.
|
||||
*
|
||||
* <p> Rendering engines of Chromium, Firefox and Webkit have a concept of "accessibility tree", which is then translated into
|
||||
* different platform-specific APIs. Accessibility namespace gives access to this Accessibility Tree.
|
||||
*
|
||||
* <p> Most of the accessibility tree gets filtered out when converting from internal browser AX Tree to Platform-specific
|
||||
* AX-Tree or by assistive technologies themselves. By default, Playwright tries to approximate this filtering, exposing
|
||||
* only the "interesting" nodes of the tree.
|
||||
*/
|
||||
public interface Accessibility {
|
||||
class SnapshotOptions {
|
||||
/**
|
||||
* Prune uninteresting nodes from the tree. Defaults to {@code true}.
|
||||
*/
|
||||
public Boolean interestingOnly;
|
||||
/**
|
||||
* The root DOM element for the snapshot. Defaults to the whole page.
|
||||
*/
|
||||
public ElementHandle root;
|
||||
|
||||
public SnapshotOptions withInterestingOnly(boolean interestingOnly) {
|
||||
this.interestingOnly = interestingOnly;
|
||||
return this;
|
||||
}
|
||||
public SnapshotOptions withRoot(ElementHandle root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Captures the current state of the accessibility tree. The returned object represents the root accessible node of the
|
||||
* page.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The Chromium accessibility tree contains nodes that go unused on most platforms and by most screen readers. Playwright
|
||||
* will discard them as well for an easier to process tree, unless {@code interestingOnly} is set to {@code false}.
|
||||
*
|
||||
* <p> An example of dumping the entire accessibility tree:
|
||||
* <pre>{@code
|
||||
* String snapshot = page.accessibility().snapshot();
|
||||
* System.out.println(snapshot);
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of logging the focused node's name:
|
||||
* <pre>{@code
|
||||
* // FIXME
|
||||
* String snapshot = page.accessibility().snapshot();
|
||||
* }</pre>
|
||||
*/
|
||||
default String snapshot() {
|
||||
return snapshot(null);
|
||||
}
|
||||
/**
|
||||
* Captures the current state of the accessibility tree. The returned object represents the root accessible node of the
|
||||
* page.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The Chromium accessibility tree contains nodes that go unused on most platforms and by most screen readers. Playwright
|
||||
* will discard them as well for an easier to process tree, unless {@code interestingOnly} is set to {@code false}.
|
||||
*
|
||||
* <p> An example of dumping the entire accessibility tree:
|
||||
* <pre>{@code
|
||||
* String snapshot = page.accessibility().snapshot();
|
||||
* System.out.println(snapshot);
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of logging the focused node's name:
|
||||
* <pre>{@code
|
||||
* // FIXME
|
||||
* String snapshot = page.accessibility().snapshot();
|
||||
* }</pre>
|
||||
*/
|
||||
String snapshot(SnapshotOptions options);
|
||||
}
|
||||
|
||||
@@ -1601,7 +1601,6 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Accessibility accessibility();
|
||||
/**
|
||||
* Adds a script which would be evaluated in one of the following scenarios:
|
||||
* <ul>
|
||||
@@ -4458,5 +4457,43 @@ public interface Page extends AutoCloseable {
|
||||
* <p> <strong>NOTE:</strong> This does not contain ServiceWorkers
|
||||
*/
|
||||
List<Worker> workers();
|
||||
/**
|
||||
* Adds one-off {@code Dialog} handler. The handler will be removed immediately after next {@code Dialog} is created.
|
||||
* <pre>{@code
|
||||
* page.onceDialog(dialog -> {
|
||||
* dialog.accept("foo");
|
||||
* });
|
||||
*
|
||||
* // prints 'foo'
|
||||
* System.out.println(page.evaluate("prompt('Enter string:')"));
|
||||
*
|
||||
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
|
||||
* System.out.println(page.evaluate("prompt('Enter string:')"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> This code above is equivalent to:
|
||||
* <pre>{@code
|
||||
* Consumer<Dialog> handler = new Consumer<Dialog>() {
|
||||
* @Override
|
||||
* public void accept(Dialog dialog) {
|
||||
* dialog.accept("foo");
|
||||
* page.offDialog(this);
|
||||
* }
|
||||
* };
|
||||
* page.onDialog(handler);
|
||||
*
|
||||
* // prints 'foo'
|
||||
* System.out.println(page.evaluate("prompt('Enter string:')"));
|
||||
*
|
||||
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
|
||||
* System.out.println(page.evaluate("prompt('Enter string:')"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param handler Receives the {@code Dialog} object, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss
|
||||
* Dialog.dismiss()} the dialog - otherwise the page will <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking">freeze</a> waiting for the
|
||||
* dialog, and actions like click will never finish.
|
||||
*/
|
||||
void onceDialog(Consumer<Dialog> handler);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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 static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class AccessibilityImpl implements Accessibility {
|
||||
private final PageImpl page;
|
||||
|
||||
AccessibilityImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String snapshot(SnapshotOptions options) {
|
||||
return page.withLogging("Accessibility.snapshot", () -> snapshotImpl(options));
|
||||
}
|
||||
|
||||
private String snapshotImpl(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 json.getAsJsonObject("rootAXNode").toString();
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
private final FrameImpl mainFrame;
|
||||
private final KeyboardImpl keyboard;
|
||||
private final MouseImpl mouse;
|
||||
private final AccessibilityImpl accessibility;
|
||||
private final TouchscreenImpl touchscreen;
|
||||
private ViewportSize viewport;
|
||||
private final Router routes = new Router();
|
||||
@@ -106,7 +105,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
keyboard = new KeyboardImpl(this);
|
||||
mouse = new MouseImpl(this);
|
||||
touchscreen = new TouchscreenImpl(this);
|
||||
accessibility = new AccessibilityImpl(this);
|
||||
frames.add(mainFrame);
|
||||
timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings);
|
||||
}
|
||||
@@ -544,11 +542,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return withLogging("Page.evalOnSelectorAll", () -> mainFrame.evalOnSelectorAllImpl(selector, pageFunction, arg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessibility accessibility() {
|
||||
return accessibility;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script) {
|
||||
withLogging("Page.addInitScript", () -> addInitScriptImpl(script));
|
||||
@@ -1327,4 +1320,18 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
public List<Worker> workers() {
|
||||
return new ArrayList<>(workers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onceDialog(Consumer<Dialog> handler) {
|
||||
onDialog(new Consumer<Dialog>() {
|
||||
@Override
|
||||
public void accept(Dialog dialog) {
|
||||
try {
|
||||
handler.accept(dialog);
|
||||
} finally {
|
||||
offDialog(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,410 +0,0 @@
|
||||
/*
|
||||
* 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 com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestAccessibility extends TestBase {
|
||||
static void assertNodeEquals(String expected, String actual) {
|
||||
JsonElement actualJson = new Gson().fromJson(actual, JsonElement.class);
|
||||
assertNodeEquals(expected, actualJson);
|
||||
}
|
||||
|
||||
static void assertNodeEquals(String expected, JsonElement actualJson) {
|
||||
assertEquals(JsonParser.parseString(expected), actualJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.setContent("<head>\n" +
|
||||
" <title>Accessibility Test</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <h1>Inputs</h1>\n" +
|
||||
" <input placeholder='Empty input' autofocus />\n" +
|
||||
" <input placeholder='readonly input' readonly />\n" +
|
||||
" <input placeholder='disabled input' disabled />\n" +
|
||||
" <input aria-label='Input with whitespace' value=' ' />\n" +
|
||||
" <input value='value only' />\n" +
|
||||
" <input aria-placeholder='placeholder' value='and a value' />\n" +
|
||||
" <div aria-hidden='true' id='desc'>This is a description!</div>\n" +
|
||||
" <input aria-placeholder='placeholder' value='and a value' aria-describedby='desc' />\n" +
|
||||
"</body>");
|
||||
// autofocus happens after a delay in chrome these days
|
||||
page.waitForFunction("() => document.activeElement.hasAttribute('autofocus')");
|
||||
|
||||
String golden = isFirefox() ? "{\n" +
|
||||
" role: 'document',\n" +
|
||||
" name: 'Accessibility Test',\n" +
|
||||
" children: [\n" +
|
||||
" {role: 'heading', name: 'Inputs', level: 1},\n" +
|
||||
" {role: 'textbox', name: 'Empty input', focused: true},\n" +
|
||||
" {role: 'textbox', name: 'readonly input', readonly: true},\n" +
|
||||
" {role: 'textbox', name: 'disabled input', disabled: true},\n" +
|
||||
" {role: 'textbox', name: 'Input with whitespace', valueString: ' '},\n" +
|
||||
" {role: 'textbox', name: '', valueString: 'value only'},\n" +
|
||||
" {role: 'textbox', name: '', valueString: 'and a value'}, // firefox doesn't use aria-placeholder for the name\n" +
|
||||
" {role: 'textbox', name: '', valueString: 'and a value', description: 'This is a description!'} // and here\n" +
|
||||
" ]\n" +
|
||||
"}" : isChromium() ? "{\n" +
|
||||
" role: 'WebArea',\n" +
|
||||
" name: 'Accessibility Test',\n" +
|
||||
" children: [\n" +
|
||||
" {role: 'heading', name: 'Inputs', level: 1},\n" +
|
||||
" {role: 'textbox', name: 'Empty input', focused: true},\n" +
|
||||
" {role: 'textbox', name: 'readonly input', readonly: true},\n" +
|
||||
" {role: 'textbox', name: 'disabled input', disabled: true},\n" +
|
||||
" {role: 'textbox', name: 'Input with whitespace', valueString: ' '},\n" +
|
||||
" {role: 'textbox', name: '', valueString: 'value only'},\n" +
|
||||
" {role: 'textbox', name: 'placeholder', valueString: 'and a value'},\n" +
|
||||
" {role: 'textbox', name: 'placeholder', valueString: 'and a value', description: 'This is a description!'}\n" +
|
||||
" ]\n" +
|
||||
"}" : "{\n" +
|
||||
" role: 'WebArea',\n" +
|
||||
" name: 'Accessibility Test',\n" +
|
||||
" children: [\n" +
|
||||
" {role: 'heading', name: 'Inputs', level: 1},\n" +
|
||||
" {role: 'textbox', name: 'Empty input', focused: true},\n" +
|
||||
" {role: 'textbox', name: 'readonly input', readonly: true},\n" +
|
||||
" {role: 'textbox', name: 'disabled input', disabled: true},\n" +
|
||||
" {role: 'textbox', name: 'Input with whitespace', valueString: ' ' },\n" +
|
||||
" {role: 'textbox', name: '', valueString: 'value only' },\n" +
|
||||
" {role: 'textbox', name: 'placeholder', valueString: 'and a value'},\n" +
|
||||
" {role: 'textbox', name: 'This is a description!',valueString: 'and a value'} // webkit uses the description over placeholder for the name\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
assertNodeEquals(golden, page.accessibility().snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithRegularText() {
|
||||
page.setContent("<div>Hello World</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
JsonObject node = snapshot.getAsJsonArray("children").get(0).getAsJsonObject();
|
||||
assertEquals(isFirefox() ? "text leaf" : "text", node.get("role").getAsString());
|
||||
assertEquals("Hello World", node.get("name").getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void roledescription() {
|
||||
page.setContent("<div tabIndex=-1 aria-roledescription='foo'>Hi</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals("foo", snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("roledescription").getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void orientation() {
|
||||
page.setContent("<a href='' role='slider' aria-orientation='vertical'>11</a>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals("vertical", snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("orientation").getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autocomplete() {
|
||||
page.setContent("<div role='textbox' aria-autocomplete='list'>hi</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals("list", snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("autocomplete").getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiselectable() {
|
||||
page.setContent("<div role='grid' tabIndex=-1 aria-multiselectable=true>hey</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals(true, snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("multiselectable").getAsBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
void keyshortcuts() {
|
||||
page.setContent("<div role='grid' tabIndex=-1 aria-keyshortcuts='foo'>hey</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals("foo", snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("keyshortcuts").getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotReportTextNodesInsideControls() {
|
||||
page.setContent("<div role='tablist'>\n" +
|
||||
" <div role='tab' aria-selected='true'><b>Tab1</b></div>\n" +
|
||||
" <div role='tab'>Tab2</div>\n" +
|
||||
"</div>");
|
||||
String golden = "{\n" +
|
||||
" role: '" + (isFirefox() ? "document" : "WebArea") + "',\n" +
|
||||
" name: '',\n" +
|
||||
" children: [{\n" +
|
||||
" role: 'tab',\n" +
|
||||
" name: 'Tab1',\n" +
|
||||
" selected: true\n" +
|
||||
" }, {\n" +
|
||||
" role: 'tab',\n" +
|
||||
" name: 'Tab2'\n" +
|
||||
" }]\n" +
|
||||
"}";
|
||||
assertNodeEquals(golden, page.accessibility().snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="com.microsoft.playwright.TestBase#isWebKit", disabledReason="skip")
|
||||
void richTextEditableFieldsShouldHaveChildren() {
|
||||
page.setContent("<div contenteditable='true'>\n" +
|
||||
" Edit this image: <img src='fakeimage.png' alt='my fake image'>\n" +
|
||||
"</div>");
|
||||
String golden = isFirefox() ? "{\n" +
|
||||
" role: 'section',\n" +
|
||||
" name: '',\n" +
|
||||
" children: [{\n" +
|
||||
" role: 'text leaf',\n" +
|
||||
" name: 'Edit this image: '\n" +
|
||||
" }, {\n" +
|
||||
" role: 'text',\n" +
|
||||
" name: 'my fake image'\n" +
|
||||
" }]\n" +
|
||||
"}" : "{\n" +
|
||||
" role: 'generic',\n" +
|
||||
" name: '',\n" +
|
||||
" valueString: 'Edit this image: ',\n" +
|
||||
" children: [{\n" +
|
||||
" role: 'text',\n" +
|
||||
" name: 'Edit this image:'\n" +
|
||||
" }, {\n" +
|
||||
" role: 'img',\n" +
|
||||
" name: 'my fake image'\n" +
|
||||
" }]\n" +
|
||||
"}";
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals(golden, snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="com.microsoft.playwright.TestBase#isWebKit", disabledReason="skip")
|
||||
void richTextEditableFieldsWithRoleShouldHaveChildren() {
|
||||
page.setContent("<div contenteditable='true' role=\"textbox\">\n" +
|
||||
" Edit this image: <img src='fakeimage.png' alt='my fake image'>\n" +
|
||||
"</div>");
|
||||
String golden = isFirefox() ? "{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: '',\n" +
|
||||
" valueString: 'Edit this image: my fake image',\n" +
|
||||
" children: [{\n" +
|
||||
" role: 'text',\n" +
|
||||
" name: 'my fake image'\n" +
|
||||
" }]\n" +
|
||||
"}" : "{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: '',\n" +
|
||||
" valueString: 'Edit this image: ',\n" +
|
||||
" children: [{\n" +
|
||||
" role: 'text',\n" +
|
||||
" name: 'Edit this image:'\n" +
|
||||
" }, {\n" +
|
||||
" role: 'img',\n" +
|
||||
" name: 'my fake image'\n" +
|
||||
" }]\n" +
|
||||
"}";
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals(golden, snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
|
||||
void plainTextFieldWithRoleShouldNotHaveChildren() {
|
||||
page.setContent("<div contenteditable='plaintext-only' role='textbox'>Edit this image:<img src='fakeimage.png' alt='my fake image'></div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: '',\n" +
|
||||
" valueString: 'Edit this image:'\n" +
|
||||
"}", snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
|
||||
void plainTextFieldWithoutRoleShouldNotHaveContent() {
|
||||
page.setContent("<div contenteditable='plaintext-only'>Edit this image:<img src='fakeimage.png' alt='my fake image'></div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'generic',\n" +
|
||||
" name: ''\n" +
|
||||
"}", snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
|
||||
void plainTextFieldWithTabindexAndWithoutRoleShouldNotHaveContent() {
|
||||
page.setContent("<div contenteditable='plaintext-only' tabIndex=0>Edit this image:<img src='fakeimage.png' alt='my fake image'></div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'generic',\n" +
|
||||
" name: ''\n" +
|
||||
"}", snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonEditableTextboxWithRoleAndTabIndexAndLabelShouldNotHaveChildren() {
|
||||
page.setContent("<div role='textbox' tabIndex=0 aria-checked='true' aria-label='my favorite textbox'>\n" +
|
||||
"this is the inner content\n" +
|
||||
"<img alt='yo' src='fakeimg.png'>\n" +
|
||||
"</div>");
|
||||
String golden = isFirefox() ? "{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: 'my favorite textbox',\n" +
|
||||
" valueString: 'this is the inner content yo'\n" +
|
||||
"}" : isChromium() ? "{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: 'my favorite textbox',\n" +
|
||||
" valueString: 'this is the inner content '\n" +
|
||||
"}" : "{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: 'my favorite textbox',\n" +
|
||||
" valueString: 'this is the inner content '\n" +
|
||||
"}";
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals(golden, snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkboxWithAndTabIndexAndLabelShouldNotHaveChildren() {
|
||||
page.setContent("<div role='checkbox' tabIndex=0 aria-checked='true' aria-label='my favorite checkbox'>\n" +
|
||||
"this is the inner content\n" +
|
||||
"<img alt='yo' src='fakeimg.png'>\n" +
|
||||
"</div>");
|
||||
String golden = "{\n" +
|
||||
" role: 'checkbox',\n" +
|
||||
" name: 'my favorite checkbox',\n" +
|
||||
" checked: 'checked'\n" +
|
||||
"}";
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals(golden, snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkboxWithoutLabelShouldNotHaveChildren() {
|
||||
page.setContent("<div role='checkbox' aria-checked='true'>\n" +
|
||||
"this is the inner content\n" +
|
||||
"<img alt='yo' src='fakeimg.png'>\n" +
|
||||
"</div>");
|
||||
String golden = isFirefox() ? "{\n" +
|
||||
" role: 'checkbox',\n" +
|
||||
" name: 'this is the inner content yo',\n" +
|
||||
" checked: 'checked'\n" +
|
||||
"}" : "{\n" +
|
||||
" role: 'checkbox',\n" +
|
||||
" name: 'this is the inner content yo',\n" +
|
||||
" checked: 'checked'\n" +
|
||||
"}";
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertNodeEquals(golden, snapshot.getAsJsonArray("children").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkAButton() {
|
||||
page.setContent("<button>My Button</button>");
|
||||
|
||||
ElementHandle button = page.querySelector("button");
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'button',\n" +
|
||||
" name: 'My Button'\n" +
|
||||
"}", page.accessibility().snapshot(new Accessibility.SnapshotOptions().withRoot(button)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkAnInput() {
|
||||
page.setContent("<input title='My Input' value='My Value'>");
|
||||
|
||||
ElementHandle input = page.querySelector("input");
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'textbox',\n" +
|
||||
" name: 'My Input',\n" +
|
||||
" valueString: 'My Value'\n" +
|
||||
"}", page.accessibility().snapshot(new Accessibility.SnapshotOptions().withRoot(input)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkOnAMenu() {
|
||||
page.setContent("<div role='menu' title='My Menu'>\n" +
|
||||
" <div role='menuitem'>First Item</div>\n" +
|
||||
" <div role='menuitem'>Second Item</div>\n" +
|
||||
" <div role='menuitem'>Third Item</div>\n" +
|
||||
"</div>");
|
||||
|
||||
ElementHandle menu = page.querySelector("div[role='menu']");
|
||||
assertNodeEquals("{\n" +
|
||||
" role: 'menu',\n" +
|
||||
" name: 'My Menu',\n" +
|
||||
" children:\n" +
|
||||
" [ { role: 'menuitem', name: 'First Item' },\n" +
|
||||
" { role: 'menuitem', name: 'Second Item' },\n" +
|
||||
" { role: 'menuitem', name: 'Third Item' } ]\n" +
|
||||
(isWebKit() ? ", orientation: 'vertical'" : "") +
|
||||
" }", page.accessibility().snapshot(new Accessibility.SnapshotOptions().withRoot(menu)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNullWhenTheElementIsNoLongerInDOM() {
|
||||
page.setContent("<button>My Button</button>");
|
||||
ElementHandle button = page.querySelector("button");
|
||||
page.evalOnSelector("button", "button => button.remove()");
|
||||
assertEquals(null, page.accessibility().snapshot(new Accessibility.SnapshotOptions().withRoot(button)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldShowUninterestingNodes() {
|
||||
page.setContent("<div id='root' role='textbox'>\n" +
|
||||
" <div>\n" +
|
||||
" hello\n" +
|
||||
" <div>\n" +
|
||||
" world\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
"</div>");
|
||||
ElementHandle root = page.querySelector("#root");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(
|
||||
new Accessibility.SnapshotOptions().withRoot(root).withInterestingOnly(false)),
|
||||
JsonObject.class);
|
||||
assertEquals("textbox", snapshot.get("role").getAsString());
|
||||
assertTrue(snapshot.get("valueString").getAsString().contains("hello"));
|
||||
assertTrue(snapshot.get("valueString").getAsString().contains("world"));
|
||||
assertNotNull(snapshot.get("children"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWhenThereIsATitle() {
|
||||
page.setContent("<title>This is the title</title>\n" +
|
||||
"<div>This is the content</div>");
|
||||
JsonObject snapshot = new Gson().fromJson(page.accessibility().snapshot(), JsonObject.class);
|
||||
assertEquals("This is the title", snapshot.get("name").getAsString());
|
||||
assertEquals("This is the content", snapshot.getAsJsonArray("children")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("name").getAsString());
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,20 @@ public class TestDialog extends TestBase {
|
||||
assertEquals("answer!", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptPromptOnce() {
|
||||
int[] callCounter = {0};
|
||||
page.onceDialog(dialog -> {
|
||||
++callCounter[0];
|
||||
assertEquals("prompt", dialog.type());
|
||||
assertEquals("question?", dialog.message());
|
||||
dialog.accept("answer!");
|
||||
});
|
||||
assertEquals("answer!", page.evaluate("prompt('question?')"));
|
||||
assertNull(page.evaluate("prompt('question?')"));
|
||||
assertEquals(1, callCounter[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDismissThePrompt() {
|
||||
page.onDialog(dialog -> {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.9.1-1614734424000
|
||||
1.9.1-1614817648000
|
||||
|
||||
Reference in New Issue
Block a user