Skip to content

Commit dd66150

Browse files
Merge pull request #69 from EvotecIT/add-option-for-non-headless-mode-and-slow-mo-in-playwright
2 parents 5a6c381 + 9db2374 commit dd66150

File tree

7 files changed

+120
-28
lines changed

7 files changed

+120
-28
lines changed

README.MD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- `Open-HTMLSession` is an alias to allow for nicer cmdlet naming alignment under different conditons
4343
- `Start-HTMLSession` is an alias to allow for nicer cmdlet naming alignment under different conditons
4444
- `Save-HTMLScreenshot` - capture page screenshots (supports `-Full`, `-Open`, clipping parameters, `-Delay` and `-Selector`)
45+
- `Visible` and `-SlowMo` parameters let you see the browser or slow down execution
4546
- `Save-HTMLPdf` - generate a PDF of a rendered page
4647
- `Save-HTMLAttachment` - download files discovered on a rendered page (optionally filter with `-Filter`, alias: `Save-HTMLDownload`)
4748
- `Invoke-HTMLNavigation` - navigate an existing session to another URL
@@ -124,6 +125,7 @@ Chromium 136.0.7103.25 (playwright build v1169) downloaded to C:\Users\USER\AppD
124125

125126
Use `-Clean` to clear the `ms-playwright` cache and re-download the runtime if needed.
126127
Use `-Full` to capture the entire document, `-Open` to automatically view the image, specify `-Delay` or `-Selector` to wait for dynamic content, or supply `-X`, `-Y`, `-Width` and `-Height` to grab a specific region.
128+
Use `-Visible` to see the browser window and `-SlowMo` to slow down actions for easier debugging.
127129

128130
The expected input is a string literal or data read from a file. The output can be PowerShell objects (classes are `HtmlNode` or `AngleSharp.Html.Dom.HtmlElement` depending on the selected engine) or strings written to `stdout`.
129131

Sources/PSParseHTML.PowerShell/CmdletGetHtmlInteractable.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public sealed class CmdletGetHtmlInteractable : AsyncPSCmdlet {
4040
[Parameter(ParameterSetName = ParameterSetFile)]
4141
public SwitchParameter Clean { get; set; }
4242

43+
/// <summary>Show the browser instead of running headless.</summary>
44+
[Parameter(ParameterSetName = ParameterSetUrl)]
45+
[Parameter(ParameterSetName = ParameterSetFile)]
46+
public SwitchParameter Visible { get; set; }
47+
48+
/// <summary>Slow down Playwright actions by the specified milliseconds.</summary>
49+
[Parameter]
50+
[ValidateRange(0, int.MaxValue)]
51+
public int SlowMo { get; set; } = 0;
52+
4353
/// <summary>Include elements hidden from view.</summary>
4454
[Parameter]
4555
public SwitchParameter IncludeHidden { get; set; }
@@ -107,7 +117,9 @@ protected override async Task ProcessRecordAsync() {
107117
Clean.IsPresent,
108118
user,
109119
pass,
110-
form).ConfigureAwait(false)) {
120+
form,
121+
headless: !Visible.IsPresent,
122+
slowMo: SlowMo).ConfigureAwait(false)) {
111123
list = await HtmlBrowserRenderer.GetInteractablesAsync(sess.Page).ConfigureAwait(false);
112124
}
113125
break;
@@ -119,7 +131,9 @@ protected override async Task ProcessRecordAsync() {
119131
Clean.IsPresent,
120132
null,
121133
null,
122-
null).ConfigureAwait(false)) {
134+
null,
135+
headless: !Visible.IsPresent,
136+
slowMo: SlowMo).ConfigureAwait(false)) {
123137
list = await HtmlBrowserRenderer.GetInteractablesAsync(sess.Page).ConfigureAwait(false);
124138
}
125139
break;

Sources/PSParseHTML.PowerShell/CmdletInvokeHtmlRendering.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ public sealed class CmdletInvokeHtmlRendering : AsyncPSCmdlet {
7777
[Parameter]
7878
public SwitchParameter NoDefault { get; set; }
7979

80+
/// <summary>Show the browser instead of running headless.</summary>
81+
[Parameter]
82+
public SwitchParameter Visible { get; set; }
83+
84+
/// <summary>Slow down Playwright actions by the specified milliseconds.</summary>
85+
[Parameter]
86+
[ValidateRange(0, int.MaxValue)]
87+
public int SlowMo { get; set; } = 0;
88+
8089
/// <inheritdoc />
8190
protected override async Task ProcessRecordAsync() {
8291
string? user = Credential?.UserName ?? Username;
@@ -102,16 +111,18 @@ protected override async Task ProcessRecordAsync() {
102111
Clean.IsPresent,
103112
user,
104113
pass,
105-
form).ConfigureAwait(false);
114+
form,
115+
headless: !Visible.IsPresent,
116+
slowMo: SlowMo).ConfigureAwait(false);
106117
if (!NoDefault.IsPresent) {
107118
SessionState.PSVariable.Set("PSParseHTML_DefaultSession", sess);
108119
}
109120
WriteObject(sess);
110121
} else if (!string.IsNullOrEmpty(OutFile)) {
111122
string outPath = FileUtilities.ResolvePath(OutFile);
112-
await HtmlBrowserRenderer.SavePageContentAsync(target, outPath, Browser, Clean.IsPresent, user, pass, form).ConfigureAwait(false);
123+
await HtmlBrowserRenderer.SavePageContentAsync(target, outPath, Browser, Clean.IsPresent, user, pass, form, !Visible.IsPresent, SlowMo).ConfigureAwait(false);
113124
} else {
114-
string html = await HtmlBrowserRenderer.GetPageContentAsync(target, Browser, Clean.IsPresent, user, pass, form).ConfigureAwait(false);
125+
string html = await HtmlBrowserRenderer.GetPageContentAsync(target, Browser, Clean.IsPresent, user, pass, form, !Visible.IsPresent, SlowMo).ConfigureAwait(false);
115126
WriteObject(html);
116127
}
117128
}

Sources/PSParseHTML.PowerShell/CmdletSaveHtmlAttachment.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ public sealed class CmdletSaveHtmlAttachment : AsyncPSCmdlet {
4040
[Parameter(ParameterSetName = ParameterSetDefault)]
4141
public SwitchParameter Clean { get; set; }
4242

43+
/// <summary>Show the browser instead of running headless.</summary>
44+
[Parameter(ParameterSetName = ParameterSetDefault)]
45+
public SwitchParameter Visible { get; set; }
46+
47+
/// <summary>Slow down Playwright actions by the specified milliseconds.</summary>
48+
[Parameter]
49+
[ValidateRange(0, int.MaxValue)]
50+
public int SlowMo { get; set; } = 0;
51+
4352
/// <summary>Optional filter applied to download URLs or file names.</summary>
4453
[Parameter]
4554
public string? Filter { get; set; }
@@ -58,7 +67,9 @@ protected override async Task ProcessRecordAsync() {
5867
FileUtilities.ResolvePath(Path),
5968
Browser,
6069
Clean.IsPresent,
61-
Filter).ConfigureAwait(false)
70+
Filter,
71+
headless: !Visible.IsPresent,
72+
slowMo: SlowMo).ConfigureAwait(false)
6273
};
6374

6475
WriteObject(files.ToArray(), true);

Sources/PSParseHTML.PowerShell/CmdletSaveHtmlPdf.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ public sealed class CmdletSaveHtmlPdf : AsyncPSCmdlet {
4343
[Parameter(ParameterSetName = ParameterSetFile)]
4444
public SwitchParameter Clean { get; set; }
4545

46+
/// <summary>Show the browser instead of running headless.</summary>
47+
[Parameter(ParameterSetName = ParameterSetDefault)]
48+
[Parameter(ParameterSetName = ParameterSetFile)]
49+
public SwitchParameter Visible { get; set; }
50+
51+
/// <summary>Slow down Playwright actions by the specified milliseconds.</summary>
52+
[Parameter]
53+
[ValidateRange(0, int.MaxValue)]
54+
public int SlowMo { get; set; } = 0;
55+
4656
/// <summary>Open the PDF after saving.</summary>
4757
[Parameter]
4858
public SwitchParameter Open { get; set; }
@@ -169,7 +179,9 @@ await HtmlBrowserRenderer.SavePagePdfAsync(
169179
FooterTemplate,
170180
PreferCssPageSize.IsPresent,
171181
outline: false,
172-
tagged: false).ConfigureAwait(false);
182+
tagged: false,
183+
headless: !Visible.IsPresent,
184+
slowMo: SlowMo).ConfigureAwait(false);
173185
break;
174186
default:
175187
await HtmlBrowserRenderer.SavePagePdfAsync(
@@ -195,7 +207,9 @@ await HtmlBrowserRenderer.SavePagePdfAsync(
195207
FooterTemplate,
196208
PreferCssPageSize.IsPresent,
197209
outline: false,
198-
tagged: false).ConfigureAwait(false);
210+
tagged: false,
211+
headless: !Visible.IsPresent,
212+
slowMo: SlowMo).ConfigureAwait(false);
199213
break;
200214
}
201215

Sources/PSParseHTML.PowerShell/CmdletSaveHtmlScreenshot.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ public sealed class CmdletSaveHtmlScreenshot : AsyncPSCmdlet {
5555
[Parameter(ParameterSetName = ParameterSetFileClip)]
5656
public SwitchParameter Clean { get; set; }
5757

58+
/// <summary>Show the browser instead of running headless.</summary>
59+
[Parameter]
60+
public SwitchParameter Visible { get; set; }
61+
62+
/// <summary>Slow down Playwright actions by the specified milliseconds.</summary>
63+
[Parameter]
64+
[ValidateRange(0, int.MaxValue)]
65+
public int SlowMo { get; set; } = 0;
66+
5867
/// <summary>Open the screenshot after saving.</summary>
5968
[Parameter]
6069
public SwitchParameter Open { get; set; }
@@ -128,7 +137,9 @@ await HtmlBrowserRenderer.CaptureScreenshotAsync(
128137
X,
129138
Y,
130139
Width,
131-
Height).ConfigureAwait(false);
140+
Height,
141+
headless: !Visible.IsPresent,
142+
slowMo: SlowMo).ConfigureAwait(false);
132143
break;
133144
case ParameterSetFileClip:
134145
await HtmlBrowserRenderer.CaptureScreenshotAsync(
@@ -142,7 +153,9 @@ await HtmlBrowserRenderer.CaptureScreenshotAsync(
142153
X,
143154
Y,
144155
Width,
145-
Height).ConfigureAwait(false);
156+
Height,
157+
headless: !Visible.IsPresent,
158+
slowMo: SlowMo).ConfigureAwait(false);
146159
break;
147160
case ParameterSetSessionClip:
148161
await HtmlBrowserRenderer.CaptureScreenshotAsync(
@@ -175,7 +188,9 @@ await HtmlBrowserRenderer.CaptureScreenshotAsync(
175188
Clean.IsPresent,
176189
Full.IsPresent,
177190
Delay,
178-
Selector).ConfigureAwait(false);
191+
Selector,
192+
headless: !Visible.IsPresent,
193+
slowMo: SlowMo).ConfigureAwait(false);
179194
break;
180195
}
181196

Sources/PSParseHTML/HtmlBrowserRenderer.cs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ private static async Task<BrowserSession> CreatePageAsync(
4545
bool clean,
4646
string? username,
4747
string? password,
48-
FormLoginOptions? formLogin) {
48+
FormLoginOptions? formLogin,
49+
bool headless = true,
50+
int slowMo = 0) {
4951
if (clean) {
5052
CleanInstallDir();
5153
}
@@ -62,7 +64,10 @@ private static async Task<BrowserSession> CreatePageAsync(
6264
_ => playwright.Chromium,
6365
};
6466

65-
var browserInstance = await type.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });
67+
var browserInstance = await type.LaunchAsync(new BrowserTypeLaunchOptions {
68+
Headless = headless,
69+
SlowMo = slowMo
70+
});
6671
BrowserNewContextOptions? contextOptions = null;
6772
if (formLogin == null && !string.IsNullOrEmpty(username) && password != null) {
6873
contextOptions = new BrowserNewContextOptions {
@@ -106,8 +111,10 @@ public static Task<BrowserSession> OpenSessionAsync(
106111
bool clean = false,
107112
string? username = null,
108113
string? password = null,
109-
FormLoginOptions? formLogin = null)
110-
=> CreatePageAsync(url, browser, clean, username, password, formLogin);
114+
FormLoginOptions? formLogin = null,
115+
bool headless = true,
116+
int slowMo = 0)
117+
=> CreatePageAsync(url, browser, clean, username, password, formLogin, headless, slowMo);
111118

112119
/// <summary>
113120
/// Disposes the specified browser session.
@@ -128,14 +135,18 @@ public static async Task<string> GetPageContentAsync(
128135
bool clean = false,
129136
string? username = null,
130137
string? password = null,
131-
FormLoginOptions? formLogin = null) {
138+
FormLoginOptions? formLogin = null,
139+
bool headless = true,
140+
int slowMo = 0) {
132141
await using BrowserSession session = await OpenSessionAsync(
133142
url,
134143
browser,
135144
clean,
136145
username,
137146
password,
138-
formLogin).ConfigureAwait(false);
147+
formLogin,
148+
headless,
149+
slowMo).ConfigureAwait(false);
139150

140151
return await session.Page.ContentAsync().ConfigureAwait(false);
141152
}
@@ -145,16 +156,18 @@ public static async Task<string> GetPageContentAsync(
145156
/// </summary>
146157
/// <param name="url">URL to load.</param>
147158
/// <param name="path">File path to write.</param>
148-
public static async Task SavePageContentAsync(
159+
public static async Task SavePageContentAsync(
149160
string url,
150161
string path,
151162
BrowserEngine browser = BrowserEngine.Chromium,
152163
bool clean = false,
153164
string? username = null,
154165
string? password = null,
155-
FormLoginOptions? formLogin = null) {
166+
FormLoginOptions? formLogin = null,
167+
bool headless = true,
168+
int slowMo = 0) {
156169
string fullPath = FileUtilities.ResolvePath(path);
157-
string content = await GetPageContentAsync(url, browser, clean, username, password, formLogin).ConfigureAwait(false);
170+
string content = await GetPageContentAsync(url, browser, clean, username, password, formLogin, headless, slowMo).ConfigureAwait(false);
158171
File.WriteAllText(fullPath, content);
159172
}
160173

@@ -186,14 +199,18 @@ public static async Task CaptureScreenshotAsync(
186199
int? clipHeight = null,
187200
string? username = null,
188201
string? password = null,
189-
FormLoginOptions? formLogin = null) {
202+
FormLoginOptions? formLogin = null,
203+
bool headless = true,
204+
int slowMo = 0) {
190205
await using BrowserSession session = await OpenSessionAsync(
191206
url,
192207
browser,
193208
clean,
194209
username,
195210
password,
196-
formLogin).ConfigureAwait(false);
211+
formLogin,
212+
headless,
213+
slowMo).ConfigureAwait(false);
197214

198215
string fullPath = FileUtilities.ResolvePath(path);
199216
await CaptureScreenshotAsync(
@@ -245,7 +262,7 @@ public static async Task CaptureScreenshotAsync(
245262
/// <summary>
246263
/// Saves a PDF of the specified page URL to disk.
247264
/// </summary>
248-
public static async Task SavePagePdfAsync(
265+
public static async Task SavePagePdfAsync(
249266
string url,
250267
string path,
251268
BrowserEngine browser = BrowserEngine.Chromium,
@@ -271,14 +288,18 @@ public static async Task SavePagePdfAsync(
271288
bool tagged = false,
272289
string? username = null,
273290
string? password = null,
274-
FormLoginOptions? formLogin = null) {
291+
FormLoginOptions? formLogin = null,
292+
bool headless = true,
293+
int slowMo = 0) {
275294
await using BrowserSession session = await OpenSessionAsync(
276295
url,
277296
browser,
278297
clean,
279298
username,
280299
password,
281-
formLogin).ConfigureAwait(false);
300+
formLogin,
301+
headless,
302+
slowMo).ConfigureAwait(false);
282303

283304
await SavePagePdfAsync(
284305
session.Page,
@@ -373,19 +394,23 @@ public static async Task SavePagePdfAsync(
373394
/// <param name="clean">Reinstall the browser runtime.</param>
374395
/// <param name="filter">Optional substring filter applied to download URLs or file names.</param>
375396
/// <returns>Paths of downloaded files.</returns>
376-
public static async Task<List<string>> SavePageDownloadsAsync(
397+
public static async Task<List<string>> SavePageDownloadsAsync(
377398
string url,
378399
string directory,
379400
BrowserEngine browser = BrowserEngine.Chromium,
380401
bool clean = false,
381-
string? filter = null) {
402+
string? filter = null,
403+
bool headless = true,
404+
int slowMo = 0) {
382405
await using BrowserSession session = await OpenSessionAsync(
383406
url,
384407
browser,
385408
clean,
386409
null,
387410
null,
388-
null).ConfigureAwait(false);
411+
null,
412+
headless,
413+
slowMo).ConfigureAwait(false);
389414
var page = session.Page;
390415
string dir = FileUtilities.ResolvePath(directory);
391416
return await SavePageDownloadsAsync(page, dir, filter).ConfigureAwait(false);

0 commit comments

Comments
 (0)