Skip to content
Open
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
2 changes: 2 additions & 0 deletions docs/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ Options:
--browser-arg TEXT Additional arguments to pass to the browser
--user-agent TEXT User-Agent header to use
--reduced-motion Emulate 'prefers-reduced-motion' media feature
--color-scheme [light|dark|no-preference]
Emulate 'prefers-color-scheme' media feature
--log-console Write console.log() to stderr
--fail Fail with an error code if a page returns an
HTTP error
Expand Down
2 changes: 2 additions & 0 deletions docs/multi.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ Options:
--browser-arg TEXT Additional arguments to pass to the browser
--user-agent TEXT User-Agent header to use
--reduced-motion Emulate 'prefers-reduced-motion' media feature
--color-scheme [light|dark|no-preference]
Emulate 'prefers-color-scheme' media feature
--log-console Write console.log() to stderr
--fail Fail with an error code if a page returns an
HTTP error
Expand Down
12 changes: 12 additions & 0 deletions docs/screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ The `--omit-background` option instructs the browser to ignore the default backg
shot-scraper https://simonwillison.net/ -o simon.png \
--width 400 --height 600 --omit-background
```
## Emulating color scheme preference

Use `--color-scheme` to emulate the `prefers-color-scheme` media feature. This is useful for capturing screenshots of pages that support dark mode:

```bash
shot-scraper https://example.com/ -o dark.png --color-scheme dark
```

The available values are `light`, `dark`, and `no-preference`.

## Interacting with the page

Sometimes it's useful to be able to manually interact with a page before the screenshot is captured.
Expand Down Expand Up @@ -366,6 +376,8 @@ Options:
--browser-arg TEXT Additional arguments to pass to the browser
--user-agent TEXT User-Agent header to use
--reduced-motion Emulate 'prefers-reduced-motion' media feature
--color-scheme [light|dark|no-preference]
Emulate 'prefers-color-scheme' media feature
--fail Fail with an error code if a page returns an
HTTP error
--skip Skip pages that return HTTP errors
Expand Down
23 changes: 23 additions & 0 deletions shot_scraper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)

BROWSERS = ("chromium", "firefox", "webkit", "chrome", "chrome-beta")
COLOR_SCHEMES = ("light", "dark", "no-preference")


def console_log(msg):
Expand Down Expand Up @@ -140,6 +141,16 @@ def reduced_motion_option(fn):
return fn


def color_scheme_option(fn):
click.option(
"--color-scheme",
type=click.Choice(COLOR_SCHEMES, case_sensitive=False),
default=None,
help="Emulate 'prefers-color-scheme' media feature",
)(fn)
return fn


@click.group(
cls=DefaultGroup,
default="shot",
Expand Down Expand Up @@ -248,6 +259,7 @@ def cli():
@browser_args_option
@user_agent_option
@reduced_motion_option
@color_scheme_option
@skip_fail_options
@bypass_csp_option
@silent_option
Expand Down Expand Up @@ -279,6 +291,7 @@ def shot(
browser_args,
user_agent,
reduced_motion,
color_scheme,
skip,
fail,
bypass_csp,
Expand Down Expand Up @@ -349,6 +362,7 @@ def shot(
user_agent=user_agent,
timeout=timeout,
reduced_motion=reduced_motion,
color_scheme=color_scheme,
bypass_csp=bypass_csp,
auth_username=auth_username,
auth_password=auth_password,
Expand Down Expand Up @@ -404,6 +418,7 @@ def _browser_context(
user_agent=None,
timeout=None,
reduced_motion=False,
color_scheme=None,
bypass_csp=False,
auth_username=None,
auth_password=None,
Expand Down Expand Up @@ -433,6 +448,8 @@ def _browser_context(
context_args["device_scale_factor"] = scale_factor
if reduced_motion:
context_args["reduced_motion"] = "reduce"
if color_scheme:
context_args["color_scheme"] = color_scheme
if user_agent is not None:
context_args["user_agent"] = user_agent
if bypass_csp:
Expand Down Expand Up @@ -487,6 +504,7 @@ def _browser_context(
@browser_args_option
@user_agent_option
@reduced_motion_option
@color_scheme_option
@log_console_option
@skip_fail_options
@silent_option
Expand Down Expand Up @@ -525,6 +543,7 @@ def multi(
browser_args,
user_agent,
reduced_motion,
color_scheme,
log_console,
skip,
fail,
Expand Down Expand Up @@ -582,6 +601,7 @@ def multi(
user_agent=user_agent,
timeout=timeout,
reduced_motion=reduced_motion,
color_scheme=color_scheme,
auth_username=auth_username,
auth_password=auth_password,
record_har_path=har_file or None,
Expand Down Expand Up @@ -964,6 +984,7 @@ def _extract_har_entry(entry, extract_dir, existing_files, file_exists_fn, zip_f
@browser_args_option
@user_agent_option
@reduced_motion_option
@color_scheme_option
@log_console_option
@skip_fail_options
@bypass_csp_option
Expand All @@ -979,6 +1000,7 @@ def javascript(
browser_args,
user_agent,
reduced_motion,
color_scheme,
log_console,
skip,
fail,
Expand Down Expand Up @@ -1035,6 +1057,7 @@ def javascript(
browser_args=browser_args,
user_agent=user_agent,
reduced_motion=reduced_motion,
color_scheme=color_scheme,
bypass_csp=bypass_csp,
auth_username=auth_username,
auth_password=auth_password,
Expand Down
30 changes: 30 additions & 0 deletions tests/test_shot_scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,36 @@ def test_html(args, expected):
assert result.output.replace("\n", "") == expected.replace("\n", "")


@pytest.mark.parametrize(
"command",
("shot", "multi", "javascript"),
)
def test_color_scheme_invalid(command):
runner = CliRunner()
result = runner.invoke(cli, [command, "-", "--color-scheme", "invalid"])
assert result.exit_code != 0
assert "Invalid value" in result.output


@pytest.mark.parametrize("color_scheme", ("light", "dark", "no-preference"))
def test_javascript_color_scheme(color_scheme):
runner = CliRunner()
with runner.isolated_filesystem():
open("index.html", "w").write(TEST_HTML)
result = runner.invoke(
cli,
[
"javascript",
"index.html",
"document.title",
"--color-scheme",
color_scheme,
],
)
assert result.exit_code == 0, str(result.exception)
assert result.output == '"Test title"\n'


@pytest.mark.parametrize(
"command,args,expected",
[
Expand Down