Skip to content

Commit a818f43

Browse files
Meir017kblok
authored andcommitted
Implemented Page.addScriptTag tests (#192)
1 parent dcaf6bb commit a818f43

File tree

6 files changed

+161
-3
lines changed

6 files changed

+161
-3
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
window.__injected = 42;
2+
window.__injectedError = new Error('hi');
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace PuppeteerSharp.Tests.Page
10+
{
11+
[Collection("PuppeteerLoaderFixture collection")]
12+
public class AddScriptTagTests : PuppeteerPageBaseTest
13+
{
14+
[Fact]
15+
public async Task ShouldThrowAnErrorIfNoOptionsAreProvided()
16+
{
17+
var exception = await Assert.ThrowsAsync<ArgumentException>(()
18+
=> Page.AddScriptTagAsync(new AddScriptTagOptions()));
19+
Assert.Equal("Provide options with a `Url`, `Path` or `Content` property", exception.Message);
20+
}
21+
22+
[Fact]
23+
public async Task ShouldWorkWithAUrl()
24+
{
25+
await Page.GoToAsync(TestConstants.EmptyPage);
26+
var scriptHandle = await Page.AddScriptTagAsync(new AddScriptTagOptions { Url = "/injectedfile.js" });
27+
Assert.NotNull(scriptHandle.AsElement());
28+
Assert.Equal(42, await Page.EvaluateExpressionAsync<int>("__injected"));
29+
}
30+
31+
[Fact]
32+
public async Task ShouldThrowAnErrorIfLoadingFromUrlFail()
33+
{
34+
await Page.GoToAsync(TestConstants.EmptyPage);
35+
var exception = await Assert.ThrowsAsync<PuppeteerException>(()
36+
=> Page.AddScriptTagAsync(new AddScriptTagOptions { Url = "/nonexistfile.js" }));
37+
Assert.Equal("Loading script from /nonexistfile.js failed", exception.Message);
38+
}
39+
40+
[Fact]
41+
public async Task ShouldWorkWithAPath()
42+
{
43+
await Page.GoToAsync(TestConstants.EmptyPage);
44+
var scriptHandle = await Page.AddScriptTagAsync(new AddScriptTagOptions
45+
{
46+
Path = Path.Combine(Directory.GetCurrentDirectory(), Path.Combine("assets", "injectedfile.js"))
47+
});
48+
Assert.NotNull(scriptHandle.AsElement());
49+
Assert.Equal(42, await Page.EvaluateExpressionAsync<int>("__injected"));
50+
}
51+
52+
[Fact]
53+
public async Task ShouldIncludeSourcemapWhenPathIsProvided()
54+
{
55+
await Page.GoToAsync(TestConstants.EmptyPage);
56+
await Page.AddScriptTagAsync(new AddScriptTagOptions
57+
{
58+
Path = Path.Combine(Directory.GetCurrentDirectory(), Path.Combine("assets", "injectedfile.js"))
59+
});
60+
var result = await Page.EvaluateExpressionAsync<string>("__injectedError.stack");
61+
Assert.Contains(Path.Combine("assets", "injectedfile.js"), result);
62+
}
63+
64+
[Fact]
65+
public async Task ShouldWorkWithContent()
66+
{
67+
await Page.GoToAsync(TestConstants.EmptyPage);
68+
var scriptHandle = await Page.AddScriptTagAsync(new AddScriptTagOptions { Content = "window.__injected = 35;" });
69+
Assert.NotNull(scriptHandle.AsElement());
70+
Assert.Equal(35, await Page.EvaluateExpressionAsync<int>("__injected"));
71+
}
72+
}
73+
}

lib/PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
<ProjectReference Include="..\PuppeteerSharp\PuppeteerSharp.csproj" />
2323
</ItemGroup>
2424
<ItemGroup>
25+
<None Update="Assets\**">
26+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
27+
</None>
2528
<None Update="test.runsettings">
2629
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2730
</None>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace PuppeteerSharp
2+
{
3+
/// <summary>
4+
/// Options used by <see cref="Page.AddScriptTagAsync(AddScriptTagOptions)"/>
5+
/// </summary>
6+
public class AddScriptTagOptions
7+
{
8+
/// <summary>
9+
/// Url of a script to be added
10+
/// </summary>
11+
public string Url { get; set; }
12+
13+
/// <summary>
14+
/// Path to the JavaScript file to be injected into frame. If its a relative path, then it is resolved relative to <see cref="System.IO.Directory.GetCurrentDirectory"/>
15+
/// </summary>
16+
public string Path { get; set; }
17+
18+
/// <summary>
19+
/// Raw JavaScript content to be injected into frame
20+
/// </summary>
21+
public string Content { get; set; }
22+
}
23+
}

lib/PuppeteerSharp/Frame.cs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
using System.Text.RegularExpressions;
36
using System.Threading.Tasks;
47

58
namespace PuppeteerSharp
@@ -158,9 +161,55 @@ internal Task<ElementHandle> AddStyleTag(dynamic options)
158161
throw new NotImplementedException();
159162
}
160163

161-
internal Task<ElementHandle> AddScriptTag(dynamic options)
164+
internal async Task<ElementHandle> AddScriptTag(AddScriptTagOptions options)
162165
{
163-
throw new NotImplementedException();
166+
const string addScriptUrl = @"async function addScriptUrl(url) {
167+
const script = document.createElement('script');
168+
script.src = url;
169+
document.head.appendChild(script);
170+
await new Promise((res, rej) => {
171+
script.onload = res;
172+
script.onerror = rej;
173+
});
174+
return script;
175+
}";
176+
const string addScriptContent = @"function addScriptContent(content) {
177+
const script = document.createElement('script');
178+
script.type = 'text/javascript';
179+
script.text = content;
180+
document.head.appendChild(script);
181+
return script;
182+
}";
183+
184+
if (!string.IsNullOrEmpty(options.Url))
185+
{
186+
var url = options.Url;
187+
try
188+
{
189+
var context = await GetExecutionContextAsync();
190+
return (await context.EvaluateFunctionHandleAsync(addScriptUrl, url)).AsElement();
191+
}
192+
catch (PuppeteerException)
193+
{
194+
throw new PuppeteerException($"Loading script from {url} failed");
195+
}
196+
}
197+
198+
if (!string.IsNullOrEmpty(options.Path))
199+
{
200+
var contents = File.ReadAllText(options.Path, Encoding.UTF8);
201+
contents += "//# sourceURL=" + options.Path.Replace("\n", string.Empty);
202+
var context = await GetExecutionContextAsync();
203+
return (await context.EvaluateFunctionHandleAsync(addScriptContent, contents)).AsElement();
204+
}
205+
206+
if (!string.IsNullOrEmpty(options.Content))
207+
{
208+
var context = await GetExecutionContextAsync();
209+
return (await context.EvaluateFunctionHandleAsync(addScriptContent, options.Content)).AsElement();
210+
}
211+
212+
throw new ArgumentException("Provide options with a `Url`, `Path` or `Content` property");
164213
}
165214

166215
internal Task<string> GetContentAsync()

lib/PuppeteerSharp/Page.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,15 @@ public async Task DeleteCookieAsync(params CookieParam[] cookies)
269269
}
270270
}
271271

272-
public async Task<ElementHandle> AddScriptTagAsync(dynamic options) => await MainFrame.AddScriptTag(options);
272+
/// <summary>
273+
/// Adds a <c><script></c> tag into the page with the desired url or content
274+
/// </summary>
275+
/// <param name="options">add script tag options</param>
276+
/// <remarks>
277+
/// Shortcut for <c>page.MainFrame.AddScriptTagAsync(options)</c>
278+
/// </remarks>
279+
/// <returns>Task which resolves to the added tag when the script's onload fires or when the script content was injected into frame</returns>
280+
public Task<ElementHandle> AddScriptTagAsync(AddScriptTagOptions options) => MainFrame.AddScriptTag(options);
273281

274282
public async Task<ElementHandle> AddStyleTagAsync(dynamic options) => await MainFrame.AddStyleTag(options);
275283

0 commit comments

Comments
 (0)