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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->99.0.4812.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Chromium <!-- GEN:chromium-version -->99.0.4837.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->95.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->96.0.1<!-- GEN:stop --> | ✅ | ✅ | ✅ |

Playwright for .NET is the official language port of [Playwright](https://playwright.dev), the library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.

Expand Down
2 changes: 1 addition & 1 deletion src/Common/Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<AssemblyVersion>1.18.0</AssemblyVersion>
<PackageVersion>$(AssemblyVersion)</PackageVersion>
<DriverVersion>1.18.0-beta-1642620709000</DriverVersion>
<DriverVersion>1.19.0-alpha-jan-31-2022</DriverVersion>
<ReleaseVersion>$(AssemblyVersion)</ReleaseVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<NoDefaultExcludes>true</NoDefaultExcludes>
Expand Down
19 changes: 1 addition & 18 deletions src/Playwright.Tests/BrowserContextAddCookiesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -511,26 +511,9 @@ await Page.EvaluateAsync(@"src => {

await Page.FirstChildFrame().EvaluateAsync<string>("document.cookie = 'username=John Doe'");
await Page.WaitForTimeoutAsync(2000);
bool allowsThirdParty = TestConstants.IsFirefox;
var cookies = await Context.CookiesAsync(new[] { Server.CrossProcessPrefix + "/grid.html" });

if (allowsThirdParty)
{
Assert.That(cookies, Has.Count.EqualTo(1));
var cookie = cookies[0];
Assert.AreEqual("127.0.0.1", cookie.Domain);
Assert.AreEqual(cookie.Expires, -1);
Assert.IsFalse(cookie.HttpOnly);
Assert.AreEqual("username", cookie.Name);
Assert.AreEqual("/", cookie.Path);
Assert.AreEqual(SameSiteAttribute.None, cookie.SameSite);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual("John Doe", cookie.Value);
}
else
{
Assert.That(cookies, Is.Empty);
}
Assert.That(cookies, Is.Empty);
}

static void AssertEqual(IEnumerable<BrowserContextCookiesResult> ea, IEnumerable<BrowserContextCookiesResult> eb)
Expand Down
8 changes: 4 additions & 4 deletions src/Playwright.Tests/BrowserContextCookiesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public async Task ShouldGetACookie()
Assert.AreEqual(-1, cookie.Expires);
Assert.IsFalse(cookie.HttpOnly);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.AreEqual((TestConstants.IsChromium || TestConstants.IsFirefox) ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
}

[PlaywrightTest("browsercontext-cookies.spec.ts", "should get a non-session cookie")]
Expand All @@ -76,7 +76,7 @@ public async Task ShouldGetANonSessionCookie()
Assert.AreEqual(new DateTimeOffset(date).ToUnixTimeSeconds(), cookie.Expires);
Assert.IsFalse(cookie.HttpOnly);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.AreEqual((TestConstants.IsChromium || TestConstants.IsFirefox) ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
}

[PlaywrightTest("browsercontext-cookies.spec.ts", "should properly report httpOnly cookie")]
Expand Down Expand Up @@ -144,7 +144,7 @@ public async Task ShouldGetMultipleCookies()
Assert.AreEqual(cookie.Expires, -1);
Assert.IsFalse(cookie.HttpOnly);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.AreEqual((TestConstants.IsChromium || TestConstants.IsFirefox) ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);

cookie = cookies[1];
Assert.AreEqual("username", cookie.Name);
Expand All @@ -154,7 +154,7 @@ public async Task ShouldGetMultipleCookies()
Assert.AreEqual(cookie.Expires, -1);
Assert.IsFalse(cookie.HttpOnly);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.AreEqual((TestConstants.IsChromium || TestConstants.IsFirefox) ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
}

[PlaywrightTest("browsercontext-cookies.spec.ts", "should get cookies from multiple urls")]
Expand Down
21 changes: 2 additions & 19 deletions src/Playwright.Tests/DefaultBrowserContext1Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async Task ContextCookiesShouldWork()
Assert.AreEqual(-1, cookie.Expires);
Assert.IsFalse(cookie.HttpOnly);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.AreEqual((TestConstants.IsChromium || TestConstants.IsFirefox) ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);

await context.DisposeAsync();
tmp.Dispose();
Expand Down Expand Up @@ -141,26 +141,9 @@ await page.EvaluateAsync(@"src => {

await page.FirstChildFrame().EvaluateAsync<string>("document.cookie = 'username=John Doe'");
await page.WaitForTimeoutAsync(2000);
bool allowsThirdParty = TestConstants.IsFirefox;
var cookies = await context.CookiesAsync(new[] { Server.CrossProcessPrefix + "/grid.html" });

if (allowsThirdParty)
{
Assert.That(cookies, Has.Count.EqualTo(1));
var cookie = cookies.First();
Assert.AreEqual("127.0.0.1", cookie.Domain);
Assert.AreEqual(cookie.Expires, -1);
Assert.IsFalse(cookie.HttpOnly);
Assert.AreEqual("username", cookie.Name);
Assert.AreEqual("/", cookie.Path);
Assert.AreEqual(TestConstants.IsChromium ? SameSiteAttribute.Lax : SameSiteAttribute.None, cookie.SameSite);
Assert.IsFalse(cookie.Secure);
Assert.AreEqual("John Doe", cookie.Value);
}
else
{
Assert.That(cookies, Is.Empty);
}
Assert.That(cookies, Is.Empty);

await context.DisposeAsync();
tmp.Dispose();
Expand Down
21 changes: 2 additions & 19 deletions src/Playwright.Tests/HeadfulTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,27 +136,10 @@ await page.EvaluateAsync(@"src => {
}");

await page.WaitForTimeoutAsync(2000);
bool allowsThirdParty = TestConstants.IsFirefox;
Assert.AreEqual(allowsThirdParty ? "username=John Doe" : string.Empty, documentCookie);
Assert.AreEqual(string.Empty, documentCookie);
var cookies = await page.Context.CookiesAsync(new[] { Server.CrossProcessPrefix + "/grid.html" });

if (allowsThirdParty)
{
Assert.That(cookies, Has.Count.EqualTo(1));
var cookie = cookies.First();
Assert.AreEqual("127.0.0.1", cookie.Domain);
Assert.AreEqual(cookie.Expires, -1);
Assert.False(cookie.HttpOnly);
Assert.AreEqual("username", cookie.Name);
Assert.AreEqual("/", cookie.Path);
Assert.AreEqual(SameSiteAttribute.None, cookie.SameSite);
Assert.False(cookie.Secure);
Assert.AreEqual("John Doe", cookie.Value);
}
else
{
Assert.IsEmpty(cookies);
}
Assert.IsEmpty(cookies);
}

[PlaywrightTest("headful.spec.ts", "should not override viewport size when passed null")]
Expand Down
14 changes: 14 additions & 0 deletions src/Playwright.Tests/Locator/LocatorConvenienceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,5 +247,19 @@ public async Task AllInnerTextsShouldWork()
await Page.SetContentAsync("<div>A</div><div>B</div><div>C</div>");
CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, (await Page.Locator("div").AllInnerTextsAsync()).ToArray());
}

[PlaywrightTest("locator-convenience.spec.ts", "should return page")]
public async Task ShouldReturnPageInstance()
{
await Page.GotoAsync(Server.Prefix + "/frames/two-frames.html");
var outer = Page.Locator("#outer");
Assert.AreEqual(outer.Page, Page);

var inner = outer.Locator("#inner");
Assert.AreEqual(inner.Page, Page);

var inFrame = Page.Frames[1].Locator("div");
Assert.AreEqual(inFrame.Page, Page);
}
}
}
2 changes: 1 addition & 1 deletion src/Playwright.Tests/PageGotoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ public async Task ShouldFailWhenReplacedByAnotherNavigation()
}
else if (TestConstants.IsWebKit)
{
StringAssert.Contains("cancelled", exception.Message);
StringAssert.Contains("Navigation interrupted by another one", exception.Message);
}
else
{
Expand Down
3 changes: 3 additions & 0 deletions src/Playwright/API/Generated/ILocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@ public partial interface ILocator
/// </param>
ILocator Nth(int index);

/// <summary><para>A page this locator belongs to.</para></summary>
IPage Page { get; }

/// <summary>
/// <para>Focuses the element, and then uses <see cref="IKeyboard.DownAsync"/> and <see cref="IKeyboard.UpAsync"/>.</para>
/// <para>
Expand Down
2 changes: 1 addition & 1 deletion src/Playwright/Core/Browser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public async Task<IBrowserContext> NewContextAsync(BrowserNewContextOptions opti
forcedColors: options.ForcedColors).ConfigureAwait(false)).Object;

context.Options = options;
context.LocalUtils = LocalUtils;
((Tracing)context.Tracing).LocalUtils = LocalUtils;

BrowserContextsList.Add(context);
return context;
Expand Down
4 changes: 1 addition & 3 deletions src/Playwright/Core/BrowserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ internal BrowserContext(IChannelOwner parent, string guid, BrowserContextInitial
e.Page?.FireResponse(e.Response);
};

_tracing = new Tracing(this);
_tracing = initializer.Tracing;
_initializer = initializer;
Browser = parent as IBrowser;
}
Expand Down Expand Up @@ -112,8 +112,6 @@ public ITracing Tracing

public IBrowser Browser { get; }

internal LocalUtils LocalUtils { get; set; }

public IReadOnlyList<IPage> Pages => PagesList;

internal float DefaultNavigationTimeout
Expand Down
2 changes: 1 addition & 1 deletion src/Playwright/Core/BrowserType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public async Task<IBrowserContext> LaunchPersistentContextAsync(string userDataD
RecordHarPath = options.RecordHarPath,
RecordHarOmitContent = options.RecordHarOmitContent,
};
context.LocalUtils = Playwright.Utils;
((Core.Tracing)context.Tracing).LocalUtils = Playwright.Utils;
return context;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Playwright/Core/Locator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public Locator(Frame parent, string selector, LocatorLocatorOptions options = nu

public ILocator Last => new Locator(_frame, $"{_selector} >> nth=-1");

IPage ILocator.Page => _frame.Page;

public async Task<IReadOnlyList<string>> AllInnerTextsAsync()
=> await EvaluateAllAsync<string[]>("ee => ee.map(e => e.innerText)").ConfigureAwait(false);

Expand Down
31 changes: 20 additions & 11 deletions src/Playwright/Core/Tracing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,41 @@
*/

using System.Threading.Tasks;
using Microsoft.Playwright.Transport;
using Microsoft.Playwright.Transport.Channels;
using Microsoft.Playwright.Transport.Protocol;

namespace Microsoft.Playwright.Core
{
internal partial class Tracing : ITracing
internal class Tracing : ChannelOwnerBase, IChannelOwner<Tracing>, ITracing
{
private readonly BrowserContext _context;
private readonly TracingChannel _channel;

public Tracing(BrowserContext context)
public Tracing(IChannelOwner parent, string guid) : base(parent, guid)
{
_context = context;
_channel = new(guid, parent.Connection, this);
}

internal LocalUtils LocalUtils { get; set; }

ChannelBase IChannelOwner.Channel => _channel;

IChannel<Tracing> IChannelOwner<Tracing>.Channel => _channel;

public async Task StartAsync(TracingStartOptions options = default)
{
await _context.Channel.TracingStartAsync(
await _channel.TracingStartAsync(
name: options?.Name,
title: options?.Title,
screenshots: options?.Screenshots,
snapshots: options?.Snapshots,
sources: options?.Sources).ConfigureAwait(false);
await _context.Channel.StartChunkAsync(options?.Title).ConfigureAwait(false);
await _channel.StartChunkAsync(options?.Title).ConfigureAwait(false);
}

public Task StartChunkAsync() => StartChunkAsync();

public Task StartChunkAsync(TracingStartChunkOptions options) => _context.Channel.StartChunkAsync(title: options?.Title);
public Task StartChunkAsync(TracingStartChunkOptions options) => _channel.StartChunkAsync(title: options?.Title);

public async Task StopChunkAsync(TracingStopChunkOptions options = null)
{
Expand All @@ -58,12 +67,12 @@ public async Task StopChunkAsync(TracingStopChunkOptions options = null)
public async Task StopAsync(TracingStopOptions options = default)
{
await StopChunkAsync(new() { Path = options?.Path }).ConfigureAwait(false);
await _context.Channel.TracingStopAsync().ConfigureAwait(false);
await _channel.TracingStopAsync().ConfigureAwait(false);
}

private async Task DoStopChunkAsync(string filePath)
{
bool isLocal = _context.Channel.Connection.IsRemote;
bool isLocal = !_channel.Connection.IsRemote;

var mode = "doNotSave";
if (!string.IsNullOrEmpty(filePath))
Expand All @@ -74,7 +83,7 @@ private async Task DoStopChunkAsync(string filePath)
mode = "compressTrace";
}

var (artifact, sourceEntries) = await _context.Channel.StopChunkAsync(mode).ConfigureAwait(false);
var (artifact, sourceEntries) = await _channel.StopChunkAsync(mode).ConfigureAwait(false);

// Not interested in artifacts.
if (string.IsNullOrEmpty(filePath))
Expand All @@ -90,7 +99,7 @@ private async Task DoStopChunkAsync(string filePath)

// Add local sources to the remote trace if necessary.
if (sourceEntries.Count > 0)
await _context.LocalUtils.ZipAsync(filePath, sourceEntries).ConfigureAwait(false);
await LocalUtils.ZipAsync(filePath, sourceEntries).ConfigureAwait(false);
}
}
}
4 changes: 2 additions & 2 deletions src/Playwright/Helpers/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ private static string GetPath(string driversPath)
internal static Dictionary<string, string> GetEnvironmentVariables()
{
var environmentVariables = new Dictionary<string, string>();
environmentVariables.Add("PW_CLI_TARGET_LANG", "csharp");
environmentVariables.Add("PW_CLI_TARGET_LANG_VERSION", $"{Environment.Version.Major}.{Environment.Version.Minor}");
environmentVariables.Add("PW_LANG_NAME", "csharp");
environmentVariables.Add("PW_LANG_NAME_VERSION", $"{Environment.Version.Major}.{Environment.Version.Minor}");
environmentVariables.Add("PW_CLI_DISPLAY_VERSION", GetSemVerPackageVersion());
return environmentVariables;
}
Expand Down
48 changes: 0 additions & 48 deletions src/Playwright/Transport/Channels/BrowserContextChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,54 +234,6 @@ internal Task SetExtraHTTPHeadersAsync(IEnumerable<KeyValuePair<string, string>>
internal Task<StorageState> GetStorageStateAsync()
=> Connection.SendMessageToServerAsync<StorageState>(Guid, "storageState", null);

internal Task TracingStartAsync(string name, string title, bool? screenshots, bool? snapshots, bool? sources)
=> Connection.SendMessageToServerAsync(
Guid,
"tracingStart",
new Dictionary<string, object>
{
["name"] = name,
["title"] = title,
["screenshots"] = screenshots,
["snapshots"] = snapshots,
["sources"] = sources,
});

internal Task TracingStopAsync()
=> Connection.SendMessageToServerAsync(
Guid,
"tracingStop");

internal Task StartChunkAsync(string title = null)
=> Connection.SendMessageToServerAsync(Guid, "tracingStartChunk", new Dictionary<string, object>
{
["title"] = title,
});

internal async Task<(Artifact Artifact, List<NameValueEntry> SourceEntries)> StopChunkAsync(string mode)
{
var result = await Connection.SendMessageToServerAsync(Guid, "tracingStopChunk", new Dictionary<string, object>
{
["mode"] = mode,
}).ConfigureAwait(false);

var artifact = result.GetObject<Artifact>("artifact", Connection);
List<NameValueEntry> sourceEntries = new() { };
if (result.Value.TryGetProperty("sourceEntries", out var sourceEntriesElement))
{
var sourceEntriesEnumerator = sourceEntriesElement.EnumerateArray();
while (sourceEntriesEnumerator.MoveNext())
{
JsonElement current = sourceEntriesEnumerator.Current;
sourceEntries.Add(current.Deserialize<NameValueEntry>(new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
}));
}
}
return (artifact, sourceEntries);
}

internal async Task<Artifact> HarExportAsync()
{
var result = await Connection.SendMessageToServerAsync(
Expand Down
3 changes: 3 additions & 0 deletions src/Playwright/Transport/Channels/ChannelOwnerType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ internal enum ChannelOwnerType
[EnumMember(Value = "stream")]
Stream,

[EnumMember(Value = "tracing")]
Tracing,

[EnumMember(Value = "fetchRequest")]
FetchRequest,

Expand Down
Loading