Skip to content

Commit

Permalink
Introduce Page.EmulateNetworkConditionsAsync (#1647)
Browse files Browse the repository at this point in the history
* feat: add page.emulateNetworkConditions

* Add Async suffix

* Add NetworkConditions defaults

* Drop PredefinedNetworkConditionsName, merge values into NetworkConditions
  • Loading branch information
Androbin authored Mar 16, 2021
1 parent caa7adf commit 2978158
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 15 deletions.
29 changes: 29 additions & 0 deletions lib/PuppeteerSharp.Tests/PageTests/EmulateNetworkConditions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Net;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace PuppeteerSharp.Tests.PageTests
{
[Collection(TestConstants.TestFixtureCollectionName)]
public class EmulateNetworkConditionsTests : PuppeteerPageBaseTest
{
public EmulateNetworkConditionsTests(ITestOutputHelper output) : base(output)
{
}

[Fact]
public async Task ShouldChangeNavigatorConnectionEffectiveType()
{
var slow3G = Puppeteer.NetworkConditions[NetworkConditions.Slow3G];
var fast3G = Puppeteer.NetworkConditions[NetworkConditions.Fast3G];

Assert.Equal("4g", await Page.EvaluateExpressionAsync<string>("window.navigator.connection.effectiveType").ConfigureAwait(false));
await Page.EmulateNetworkConditionsAsync(fast3G);
Assert.Equal("3g", await Page.EvaluateExpressionAsync<string>("window.navigator.connection.effectiveType").ConfigureAwait(false));
await Page.EmulateNetworkConditionsAsync(slow3G);
Assert.Equal("2g", await Page.EvaluateExpressionAsync<string>("window.navigator.connection.effectiveType").ConfigureAwait(false));
await Page.EmulateNetworkConditionsAsync(null);
}
}
}
7 changes: 7 additions & 0 deletions lib/PuppeteerSharp/InternalNetworkConditions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace PuppeteerSharp
{
internal class InternalNetworkConditions : NetworkConditions
{
public bool Offline { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ internal class NetworkEmulateNetworkConditionsRequest
{
public bool Offline { get; set; }

public int Latency { get; set; }
public double Latency { get; set; }

public int DownloadThroughput { get; set; }
public double DownloadThroughput { get; set; }

public int UploadThroughput { get; set; }
public double UploadThroughput { get; set; }
}
}
33 changes: 33 additions & 0 deletions lib/PuppeteerSharp/NetworkConditions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace PuppeteerSharp
{
/// <summary>
/// Options to be used in <see cref="Page.EmulateNetworkConditionsAsync(NetworkConditions)"/>
/// </summary>
public class NetworkConditions
{
/// <summary>
/// Key to be used with <see cref="Puppeteer.NetworkConditions()"/>
/// </summary>
public const string Slow3G = "Slow 3G";

/// <summary>
/// Key to be used with <see cref="Puppeteer.NetworkConditions()"/>
/// </summary>
public const string Fast3G = "Fast 3G";

/// <summary>
/// Download speed (bytes/s), `-1` to disable
/// </summary>
public double Download { get; set; } = -1;

/// <summary>
/// Upload speed (bytes/s), `-1` to disable
/// </summary>
public double Upload { get; set; } = -1;

/// <summary>
/// Latency (ms), `0` to disable
/// </summary>
public double Latency { get; set; } = 0;
}
}
37 changes: 25 additions & 12 deletions lib/PuppeteerSharp/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ internal class NetworkManager
private readonly ConcurrentDictionary<string, string> _requestIdToInterceptionId = new ConcurrentDictionary<string, string>();
private readonly ILogger _logger;
private Dictionary<string, string> _extraHTTPHeaders;
private bool _offine;
private Credentials _credentials;
private readonly List<string> _attemptedAuthentications = new List<string>();
private bool _userRequestInterceptionEnabled;
private bool _protocolRequestInterceptionEnabled;
private readonly bool _ignoreHTTPSErrors;
private bool _userCacheDisabled;
private InternalNetworkConditions _emulatedNetworkConditions = new InternalNetworkConditions
{
Offline = false,
Upload = -1,
Download = -1,
Latency = 0,
};
#endregion

internal NetworkManager(CDPSession client, bool ignoreHTTPSErrors, FrameManager frameManager)
Expand Down Expand Up @@ -90,20 +96,27 @@ internal Task SetExtraHTTPHeadersAsync(Dictionary<string, string> extraHTTPHeade

internal async Task SetOfflineModeAsync(bool value)
{
if (_offine != value)
{
_offine = value;
_emulatedNetworkConditions.Offline = value;
await UpdateNetworkConditionsAsync().ConfigureAwait(false);
}

await _client.SendAsync("Network.emulateNetworkConditions", new NetworkEmulateNetworkConditionsRequest
{
Offline = value,
Latency = 0,
DownloadThroughput = -1,
UploadThroughput = -1
}).ConfigureAwait(false);
}
internal async Task EmulateNetworkConditionsAsync(NetworkConditions networkConditions)
{
_emulatedNetworkConditions.Upload = networkConditions?.Upload ?? -1;
_emulatedNetworkConditions.Download = networkConditions?.Download ?? -1;
_emulatedNetworkConditions.Latency = networkConditions?.Latency ?? 0;
await UpdateNetworkConditionsAsync().ConfigureAwait(false);
}

private Task UpdateNetworkConditionsAsync()
=> _client.SendAsync("Network.emulateNetworkConditions", new NetworkEmulateNetworkConditionsRequest
{
Offline = _emulatedNetworkConditions.Offline,
Latency = _emulatedNetworkConditions.Latency,
UploadThroughput = _emulatedNetworkConditions.Upload,
DownloadThroughput = _emulatedNetworkConditions.Download,
});

internal Task SetUserAgentAsync(string userAgent)
=> _client.SendAsync("Network.setUserAgentOverride", new NetworkSetUserAgentOverrideRequest
{
Expand Down
10 changes: 10 additions & 0 deletions lib/PuppeteerSharp/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,16 @@ public Task SetRequestInterceptionAsync(bool value)
/// <param name="value">When <c>true</c> enables offline mode for the page.</param>
public Task SetOfflineModeAsync(bool value) => FrameManager.NetworkManager.SetOfflineModeAsync(value);

/// <summary>
/// Emulates network conditions
/// </summary>
/// <param name="networkConditions">Passing <c>null</c> disables network condition emulation.</param>
/// <returns>Result task</returns>
/// <remarks>
/// **NOTE** This does not affect WebSockets and WebRTC PeerConnections (see https://crbug.com/563644)
/// </remarks>
public Task EmulateNetworkConditionsAsync(NetworkConditions networkConditions) => FrameManager.NetworkManager.EmulateNetworkConditionsAsync(networkConditions);

/// <summary>
/// Returns the page's cookies
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions lib/PuppeteerSharp/PredefinedNetworkConditions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace PuppeteerSharp
{
/// <summary>
/// Predefined network conditions.
/// </summary>
public static class PredefinedNetworkConditions
{
private static readonly Dictionary<string, NetworkConditions> Conditions = new Dictionary<string, NetworkConditions>
{
[NetworkConditions.Slow3G] = new NetworkConditions
{
Download = ((500 * 1000) / 8) * 0.8,
Upload = ((500 * 1000) / 8) * 0.8,
Latency = 400 * 5,
},
[NetworkConditions.Fast3G] = new NetworkConditions
{
Download = ((1.6 * 1000 * 1000) / 8) * 0.9,
Upload = ((750 * 1000) / 8) * 0.9,
Latency = 150 * 3.75,
},
};

private static Lazy<IReadOnlyDictionary<string, NetworkConditions>> _readOnlyConditions =
new Lazy<IReadOnlyDictionary<string, NetworkConditions>>(() => new ReadOnlyDictionary<string, NetworkConditions>(Conditions));

internal static IReadOnlyDictionary<string, NetworkConditions> ToReadOnly() => _readOnlyConditions.Value;
}
}
18 changes: 18 additions & 0 deletions lib/PuppeteerSharp/Puppeteer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,23 @@ public static BrowserFetcher CreateBrowserFetcher(BrowserFetcherOptions options)
/// </code>
/// </example>
public static IReadOnlyDictionary<DeviceDescriptorName, DeviceDescriptor> Devices => DeviceDescriptors.ToReadOnly();

/// <summary>
/// Returns a list of network conditions to be used with <seealso cref="Page.EmulateNetworkConditionsAsync(NetworkConditions)"/>.
/// Actual list of conditions can be found in <seealso cref="PredefinedNetworkConditions.Conditions"/>.
/// </summary>
/// <example>
/// <code>
///<![CDATA[
/// var slow3G = Puppeteer.NetworkConditions["Slow 3G"];
/// using(var page = await browser.NewPageAsync())
/// {
/// await page.EmulateNetworkConditionsAsync(slow3G);
/// await page.goto('https://www.google.com');
/// }
/// ]]>
/// </code>
/// </example>
public static IReadOnlyDictionary<string, NetworkConditions> NetworkConditions => PredefinedNetworkConditions.ToReadOnly();
}
}

0 comments on commit 2978158

Please sign in to comment.