Skip to content

Commit 444be11

Browse files
authored
Implement ElementHandle.Click (#205)
1 parent b37ef47 commit 444be11

File tree

7 files changed

+111
-24
lines changed

7 files changed

+111
-24
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Xunit;
4+
5+
namespace PuppeteerSharp.Tests.ElementHandleTests
6+
{
7+
[Collection("PuppeteerLoaderFixture collection")]
8+
public class ClickTests : PuppeteerPageBaseTest
9+
{
10+
[Fact]
11+
public async Task ShouldWork()
12+
{
13+
await Page.GoToAsync(TestConstants.ServerUrl + "/input/button.html");
14+
var button = await Page.QuerySelectorAsync("button");
15+
await button.ClickAsync();
16+
Assert.Equal("Clicked", await Page.EvaluateExpressionAsync<string>("result"));
17+
}
18+
19+
[Fact]
20+
public async Task ShouldWorkForShadowDomV1()
21+
{
22+
await Page.GoToAsync(TestConstants.ServerUrl + "/shadow.html");
23+
var buttonHandle = (await Page.EvaluateExpressionHandleAsync("button")) as ElementHandle;
24+
await buttonHandle.ClickAsync();
25+
Assert.True(await Page.EvaluateExpressionAsync<bool>("clicked"));
26+
}
27+
28+
[Fact]
29+
public async Task ShouldWorkForTextNodes()
30+
{
31+
await Page.GoToAsync(TestConstants.ServerUrl + "/input/button.html");
32+
var buttonTextNode = (await Page.EvaluateExpressionHandleAsync(
33+
"document.querySelector('button').firstChild")) as ElementHandle;
34+
var exception = await Assert.ThrowsAsync<PuppeteerException>(async () => await buttonTextNode.ClickAsync());
35+
Assert.Equal("Node is not of type HTMLElement", exception.Message);
36+
}
37+
38+
[Fact]
39+
public async Task ShouldThrowForDetachedNodes()
40+
{
41+
await Page.GoToAsync(TestConstants.ServerUrl + "/input/button.html");
42+
var button = await Page.QuerySelectorAsync("button");
43+
await Page.EvaluateFunctionAsync("button => button.remove()", button);
44+
var exception = await Assert.ThrowsAsync<PuppeteerException>(async () => await button.ClickAsync());
45+
Assert.Equal("Node is detached from document", exception.Message);
46+
}
47+
48+
[Fact]
49+
public async Task ShouldThrowForHiddenNodes()
50+
{
51+
await Page.GoToAsync(TestConstants.ServerUrl + "/input/button.html");
52+
var button = await Page.QuerySelectorAsync("button");
53+
await Page.EvaluateFunctionAsync("button => button.style.display = 'none'", button);
54+
var exception = await Assert.ThrowsAsync<PuppeteerException>(async () => await button.ClickAsync());
55+
Assert.Equal("Node is not visible", exception.Message);
56+
}
57+
58+
[Fact]
59+
public async Task ShouldThrowForRecursivelyHiddenNodes()
60+
{
61+
await Page.GoToAsync(TestConstants.ServerUrl + "/input/button.html");
62+
var button = await Page.QuerySelectorAsync("button");
63+
await Page.EvaluateFunctionAsync("button => button.parentElement.style.display = 'none'", button);
64+
var exception = await Assert.ThrowsAsync<PuppeteerException>(async () => await button.ClickAsync());
65+
Assert.Equal("Node is not visible", exception.Message);
66+
}
67+
68+
[Fact]
69+
public async Task ShouldThrowForBrElements()
70+
{
71+
await Page.SetContentAsync("hello<br>goodbye");
72+
var br = await Page.QuerySelectorAsync("br");
73+
var exception = await Assert.ThrowsAsync<PuppeteerException>(async () => await br.ClickAsync());
74+
Assert.Equal("Node is not visible", exception.Message);
75+
}
76+
}
77+
}

lib/PuppeteerSharp.Tests/JSHandleTests/AsElementTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ public class AsElementTests : PuppeteerPageBaseTest
1010
public async Task ShouldWork()
1111
{
1212
var aHandle = await Page.EvaluateExpressionHandleAsync("document.body");
13-
var element = aHandle.AsElement();
13+
var element = aHandle as ElementHandle;
1414
Assert.NotNull(element);
1515
}
1616

1717
[Fact]
1818
public async Task ShouldReturnNullForNonElements()
1919
{
2020
var aHandle = await Page.EvaluateExpressionHandleAsync("2");
21-
var element = aHandle.AsElement();
21+
var element = aHandle as ElementHandle;
2222
Assert.Null(element);
2323
}
2424

@@ -27,7 +27,7 @@ public async Task ShouldReturnElementHandleForTextNodes()
2727
{
2828
await Page.SetContentAsync("<div>ee!</div>");
2929
var aHandle = await Page.EvaluateExpressionHandleAsync("document.querySelector('div').firstChild");
30-
var element = aHandle.AsElement();
30+
var element = aHandle as ElementHandle;
3131
Assert.NotNull(element);
3232
Assert.NotNull(await Page.EvaluateFunctionAsync("e => e.nodeType === HTMLElement.TEXT_NODE", element));
3333
}

lib/PuppeteerSharp.Tests/Page/AddScriptTagTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task ShouldWorkWithAUrl()
2121
{
2222
await Page.GoToAsync(TestConstants.EmptyPage);
2323
var scriptHandle = await Page.AddScriptTagAsync(new AddTagOptions { Url = "/injectedfile.js" });
24-
Assert.NotNull(scriptHandle.AsElement());
24+
Assert.NotNull(scriptHandle as ElementHandle);
2525
Assert.Equal(42, await Page.EvaluateExpressionAsync<int>("__injected"));
2626
}
2727

@@ -42,7 +42,7 @@ public async Task ShouldWorkWithAPath()
4242
{
4343
Path = Path.Combine(Directory.GetCurrentDirectory(), Path.Combine("assets", "injectedfile.js"))
4444
});
45-
Assert.NotNull(scriptHandle.AsElement());
45+
Assert.NotNull(scriptHandle as ElementHandle);
4646
Assert.Equal(42, await Page.EvaluateExpressionAsync<int>("__injected"));
4747
}
4848

@@ -63,7 +63,7 @@ public async Task ShouldWorkWithContent()
6363
{
6464
await Page.GoToAsync(TestConstants.EmptyPage);
6565
var scriptHandle = await Page.AddScriptTagAsync(new AddTagOptions { Content = "window.__injected = 35;" });
66-
Assert.NotNull(scriptHandle.AsElement());
66+
Assert.NotNull(scriptHandle as ElementHandle);
6767
Assert.Equal(35, await Page.EvaluateExpressionAsync<int>("__injected"));
6868
}
6969
}

lib/PuppeteerSharp.Tests/Page/AddStyleTagTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task ShouldWorkWithAUrl()
2121
{
2222
await Page.GoToAsync(TestConstants.EmptyPage);
2323
var styleHandle = await Page.AddStyleTagAsync(new AddTagOptions { Url = "/injectedstyle.css" });
24-
Assert.NotNull(styleHandle.AsElement());
24+
Assert.NotNull(styleHandle as ElementHandle);
2525
Assert.Equal("rgb(255, 0, 0)", await Page.EvaluateExpressionAsync(
2626
"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')"));
2727
}
@@ -40,7 +40,7 @@ public async Task ShouldWorkWithAPath()
4040
{
4141
await Page.GoToAsync(TestConstants.EmptyPage);
4242
var styleHandle = await Page.AddStyleTagAsync(new AddTagOptions { Path = "assets/injectedstyle.css" });
43-
Assert.NotNull(styleHandle.AsElement());
43+
Assert.NotNull(styleHandle as ElementHandle);
4444
Assert.Equal("rgb(255, 0, 0)", await Page.EvaluateExpressionAsync(
4545
"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')"));
4646
}
@@ -63,7 +63,7 @@ public async Task ShouldWorkWithContent()
6363
{
6464
await Page.GoToAsync(TestConstants.EmptyPage);
6565
var styleHandle = await Page.AddStyleTagAsync(new AddTagOptions { Content = "body { background-color: green; }" });
66-
Assert.NotNull(styleHandle.AsElement());
66+
Assert.NotNull(styleHandle as ElementHandle);
6767
Assert.Equal("rgb(0, 128, 0)", await Page.EvaluateExpressionAsync(
6868
"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')"));
6969
}

lib/PuppeteerSharp/ElementHandle.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using PuppeteerSharp.Input;
2+
using System;
23
using System.Collections.Generic;
34
using System.IO;
45
using System.Linq;
@@ -16,8 +17,6 @@ public ElementHandle(ExecutionContext context, Session client, object remoteObje
1617
Page = page;
1718
}
1819

19-
public override ElementHandle AsElement() => this;
20-
2120
/// <summary>
2221
/// Scrolls element into view if needed, and then uses <see cref="Page.Mouse"/> to hover over the center of the element.
2322
/// </summary>
@@ -118,7 +117,7 @@ internal async Task<ElementHandle> QuerySelectorAsync(string selector)
118117
"(element, selector) => element.querySelector(selector)",
119118
this, selector);
120119

121-
var element = handle.AsElement();
120+
var element = handle as ElementHandle;
122121
if (element != null)
123122
{
124123
return element;
@@ -192,10 +191,24 @@ private async Task ScrollIntoViewIfNeededAsync()
192191

193192
private async Task<BoundingBox> BoundingBoxAsync()
194193
{
195-
var result = await _client.SendAsync("DOM.getBoxModel", new { objectId = RemoteObject.objectId.ToString() });
194+
dynamic result = null;
195+
196+
try
197+
{
198+
result = await _client.SendAsync("DOM.getBoxModel", new
199+
{
200+
objectId = RemoteObject.objectId.ToString()
201+
});
202+
}
203+
catch (PuppeteerException ex)
204+
{
205+
Console.WriteLine(ex.Message);
206+
}
196207

197208
if (result == null)
209+
{
198210
return null;
211+
}
199212

200213
var quad = result.model.border.ToObject<decimal[]>();
201214

@@ -205,7 +218,6 @@ private async Task<BoundingBox> BoundingBoxAsync()
205218
var height = new[] { quad[1], quad[3], quad[5], quad[7] }.Max() - y;
206219

207220
return new BoundingBox(x, y, width, height);
208-
209221
}
210222

211223
private class BoundingBox

lib/PuppeteerSharp/Frame.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ internal async Task<ElementHandle> AddStyleTag(AddTagOptions options)
186186
try
187187
{
188188
var context = await GetExecutionContextAsync();
189-
return (await context.EvaluateFunctionHandleAsync(addStyleUrl, url)).AsElement();
189+
return (await context.EvaluateFunctionHandleAsync(addStyleUrl, url)) as ElementHandle;
190190
}
191191
catch (PuppeteerException)
192192
{
@@ -199,13 +199,13 @@ internal async Task<ElementHandle> AddStyleTag(AddTagOptions options)
199199
var contents = File.ReadAllText(options.Path, Encoding.UTF8);
200200
contents += "//# sourceURL=" + options.Path.Replace("\n", string.Empty);
201201
var context = await GetExecutionContextAsync();
202-
return (await context.EvaluateFunctionHandleAsync(addStyleContent, contents)).AsElement();
202+
return (await context.EvaluateFunctionHandleAsync(addStyleContent, contents)) as ElementHandle;
203203
}
204204

205205
if (!string.IsNullOrEmpty(options.Content))
206206
{
207207
var context = await GetExecutionContextAsync();
208-
return (await context.EvaluateFunctionHandleAsync(addStyleContent, options.Content)).AsElement();
208+
return (await context.EvaluateFunctionHandleAsync(addStyleContent, options.Content)) as ElementHandle;
209209
}
210210

211211
throw new ArgumentException("Provide options with a `Url`, `Path` or `Content` property");
@@ -237,7 +237,7 @@ internal async Task<ElementHandle> AddScriptTag(AddTagOptions options)
237237
try
238238
{
239239
var context = await GetExecutionContextAsync();
240-
return (await context.EvaluateFunctionHandleAsync(addScriptUrl, url)).AsElement();
240+
return (await context.EvaluateFunctionHandleAsync(addScriptUrl, url)) as ElementHandle;
241241
}
242242
catch (PuppeteerException)
243243
{
@@ -250,13 +250,13 @@ internal async Task<ElementHandle> AddScriptTag(AddTagOptions options)
250250
var contents = File.ReadAllText(options.Path, Encoding.UTF8);
251251
contents += "//# sourceURL=" + options.Path.Replace("\n", string.Empty);
252252
var context = await GetExecutionContextAsync();
253-
return (await context.EvaluateFunctionHandleAsync(addScriptContent, contents)).AsElement();
253+
return (await context.EvaluateFunctionHandleAsync(addScriptContent, contents)) as ElementHandle;
254254
}
255255

256256
if (!string.IsNullOrEmpty(options.Content))
257257
{
258258
var context = await GetExecutionContextAsync();
259-
return (await context.EvaluateFunctionHandleAsync(addScriptContent, options.Content)).AsElement();
259+
return (await context.EvaluateFunctionHandleAsync(addScriptContent, options.Content)) as ElementHandle;
260260
}
261261

262262
throw new ArgumentException("Provide options with a `Url`, `Path` or `Content` property");
@@ -366,7 +366,7 @@ function hasVisibleBoundingBox() {
366366
Timeout = options.Timeout,
367367
Polling = polling
368368
}, selector, options.Visible, options.Hidden);
369-
return handle.AsElement();
369+
return handle as ElementHandle;
370370
}
371371

372372
internal Task<string[]> SelectAsync(string selector, params string[] values)
@@ -394,7 +394,7 @@ private async Task<ElementHandle> GetDocument()
394394
_documentCompletionSource = new TaskCompletionSource<ElementHandle>();
395395
var context = await GetExecutionContextAsync();
396396
var document = await context.EvaluateExpressionHandleAsync("document");
397-
_documentCompletionSource.SetResult(document.AsElement());
397+
_documentCompletionSource.SetResult(document as ElementHandle);
398398
}
399399
return await _documentCompletionSource.Task;
400400
}

lib/PuppeteerSharp/JSHandle.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ public async Task<T> JsonValueAsync<T>()
103103
return (T)Helper.ValueFromRemoteObject<T>(RemoteObject);
104104
}
105105

106-
public virtual ElementHandle AsElement() => null;
107-
108106
public async Task DisposeAsync()
109107
{
110108
if (Disposed)

0 commit comments

Comments
 (0)