Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,15 +392,20 @@ Test directory structure demonstrates comprehensive coverage:
When running tests, always build first and then use the `--no-build` flag to avoid rebuilding during test execution. This provides faster and more reliable test runs:
Always be explicit with the browser and protocol you want to test using ENV variables BROWSER=FIREFOX|CHROME and PROTOCOL=bidi|cdp

**IMPORTANT: Chrome should ALWAYS use CDP protocol. Firefox can use either CDP or BiDi.**

```bash
# Build the test project first
# Build the test project first with Firefox and BiDi
BROWSER=FIREFOX PROTOCOL=bidi dotnet build PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj

# Then run tests with --no-build flag
BROWSER=CHROME PROTOCOL=bidi dotnet test PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj --filter "FullyQualifiedName~TestName" --no-build -- NUnit.TestOutputXml=TestResults
BROWSER=FIREFOX PROTOCOL=bidi dotnet test PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj --filter "FullyQualifiedName~TestName" --no-build -- NUnit.TestOutputXml=TestResults

# Chrome should ALWAYS use CDP protocol
BROWSER=CHROME PROTOCOL=cdp dotnet build PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj && BROWSER=CHROME PROTOCOL=cdp dotnet test PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj --filter "FullyQualifiedName~TestName" --no-build -- NUnit.TestOutputXml=TestResults

# Can also chain them together
BROWSER=CHROME PROTOCOL=cdp dotnet build PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj && dotnet test PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj --filter "FullyQualifiedName~TestName" --no-build -- NUnit.TestOutputXml=TestResults
# Firefox should ALWAYS use bidi protocol
BROWSER=FIREFOX PROTOCOL=bidi dotnet build PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj && BROWSER=FIREFOX PROTOCOL=cdp dotnet test PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj --filter "FullyQualifiedName~TestName" --no-build -- NUnit.TestOutputXml=TestResults
```

You can switch between CDP and Bidi by changing the PuppeteerTestAttribute.IsCdp property.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1035,21 +1035,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[page.spec] *Page.select*",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[page.spec] *Page.setBypassCSP*",
Expand Down
1 change: 1 addition & 0 deletions lib/PuppeteerSharp.TestServer/wwwroot/input/select.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</head>
<body>
<select>
<option value="">Empty</option>
<option value="black">Black</option>
<option value="blue">Blue</option>
<option value="brown">Brown</option>
Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp.Tests/PageTests/SelectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ public async Task ShouldDeselectAllOptionsWhenPassedNoValuesForASelectWithoutMul
await Page.GoToAsync(TestConstants.ServerUrl + "/input/select.html");
await Page.SelectAsync("select", "blue", "black", "magenta");
await Page.SelectAsync("select");
Assert.That(await Page.QuerySelectorAsync("select").EvaluateFunctionAsync<bool>(
"select => Array.from(select.options).every(option => !option.selected)"), Is.True);
Assert.That(await Page.QuerySelectorAsync("select").EvaluateFunctionAsync<string>(
"select => Array.from(select.options).filter(option => option.selected)[0].value"), Is.EqualTo(""));
}
}
}
3 changes: 1 addition & 2 deletions lib/PuppeteerSharp/Bidi/BidiRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Numerics;
Expand Down Expand Up @@ -214,7 +213,7 @@ private async Task<EvaluateResultSuccess> EvaluateAsync(bool returnByValue, bool
if (result.ResultType == EvaluateResultType.Exception)
{
// TODO: Improve text details
throw new EvaluateException(((EvaluateResultException)result).ExceptionDetails.Text);
throw new EvaluationFailedException(((EvaluateResultException)result).ExceptionDetails.Text);
}

return result as EvaluateResultSuccess;
Expand Down
59 changes: 44 additions & 15 deletions lib/PuppeteerSharp/ElementHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,24 +472,53 @@ public Task<bool> IsIntersectingViewportAsync(decimal threshold)

/// <inheritdoc/>
public Task<string[]> SelectAsync(params string[] values)
=> BindIsolatedHandleAsync<string[], ElementHandle>(handle => handle.EvaluateFunctionAsync<string[]>(
@"(element, values) =>
{
if (element.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');

const options = Array.from(element.options);
element.value = undefined;
for (const option of options) {
option.selected = values.includes(option.value);
if (option.selected && !element.multiple)
{
if (values == null)
{
throw new ArgumentNullException(nameof(values));
}

foreach (var value in values)
{
if (value == null)
{
throw new ArgumentException($"Values must be strings. Found value \"null\" of type \"null\"");
}
}

return BindIsolatedHandleAsync<string[], ElementHandle>(handle => handle.EvaluateFunctionAsync<string[]>(
@"(element, vals) => {
const values = new Set(vals);
if (!(element instanceof HTMLSelectElement)) {
throw new Error('Element is not a <select> element.');
}

const selectedValues = new Set();
if (!element.multiple) {
for (const option of element.options) {
option.selected = false;
}
for (const option of element.options) {
if (values.has(option.value)) {
option.selected = true;
selectedValues.add(option.value);
break;
}
}
element.dispatchEvent(new Event('input', { 'bubbles': true }));
element.dispatchEvent(new Event('change', { 'bubbles': true }));
return options.filter(option => option.selected).map(option => option.value);
}",
} else {
for (const option of element.options) {
option.selected = values.has(option.value);
if (option.selected) {
selectedValues.add(option.value);
}
}
}
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
return [...selectedValues.values()];
}",
new object[] { values }));
}

/// <inheritdoc/>
public Task<DragData> DragAsync(decimal x, decimal y)
Expand Down
Loading