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
73 changes: 73 additions & 0 deletions atest/acceptance/keywords/screenshot_fullpage.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
*** Settings ***
Documentation Tests fullpage screenshots
Suite Setup Go To Page "forms.html"
Resource ../resource.robot
Force Tags Known Issue Internet Explorer

*** Test Cases ***
Capture fullpage screenshot to default location
[Tags] NoGrid
[Documentation]
... LOG 1:5 </td></tr><tr><td colspan="3"><a href="selenium-fullpage-screenshot-1.png"><img src="selenium-fullpage-screenshot-1.png" width="800px"></a>
... LOG 7:5 </td></tr><tr><td colspan="3"><a href="selenium-fullpage-screenshot-2.png"><img src="selenium-fullpage-screenshot-2.png" width="800px"></a>
[Setup] Remove Files ${OUTPUTDIR}/selenium-fullpage-screenshot-*.png
${file} = Capture Fullpage Screenshot
${count} = Count Files In Directory ${OUTPUTDIR} selenium-fullpage-screenshot-*.png
Should Be Equal As Integers ${count} 1
Should Be Equal ${file} ${OUTPUTDIR}${/}selenium-fullpage-screenshot-1.png
Click Link Relative
Wait Until Page Contains Element tag=body
Capture Fullpage Screenshot
${count} = Count Files In Directory ${OUTPUTDIR} selenium-fullpage-screenshot-*.png
Should Be Equal As Integers ${count} 2

Capture fullpage screenshot to custom file
[Setup] Remove Files ${OUTPUTDIR}/custom-fullpage-screenshot.png
Capture Fullpage Screenshot custom-fullpage-screenshot.png
File Should Exist ${OUTPUTDIR}/custom-fullpage-screenshot.png

Capture fullpage screenshot to custom directory
[Setup] Remove Files ${TEMPDIR}/seleniumlibrary-fullpage-screenshot-test.png
Create Directory ${TEMPDIR}
Set Screenshot Directory ${TEMPDIR}
Capture Fullpage Screenshot seleniumlibrary-fullpage-screenshot-test.png
File Should Exist ${TEMPDIR}/seleniumlibrary-fullpage-screenshot-test.png

Capture fullpage screenshot with index
[Setup] Remove Files ${OUTPUTDIR}/fullpage-screenshot-*.png
Capture Fullpage Screenshot fullpage-screenshot-{index}.png
Capture Fullpage Screenshot fullpage-screenshot-{index}.png
File Should Exist ${OUTPUTDIR}/fullpage-screenshot-1.png
File Should Exist ${OUTPUTDIR}/fullpage-screenshot-2.png

Capture fullpage screenshot with formatted index
[Setup] Remove Files ${OUTPUTDIR}/fullpage-screenshot-*.png
Capture Fullpage Screenshot fullpage-screenshot-{index:03}.png
File Should Exist ${OUTPUTDIR}/fullpage-screenshot-001.png

Capture fullpage screenshot embedded
[Setup] Set Screenshot Directory EMBED
${result} = Capture Fullpage Screenshot
Should Be Equal ${result} EMBED

Capture fullpage screenshot base64
[Setup] Set Screenshot Directory BASE64
${result} = Capture Fullpage Screenshot
Should Not Be Empty ${result}
Should Match Regexp ${result} ^[A-Za-z0-9+/=]+$

Capture fullpage screenshot with EMBED filename
[Setup] Set Screenshot Directory EMBED
${result} = Capture Fullpage Screenshot EMBED
Should Be Equal ${result} EMBED

Capture fullpage screenshot with BASE64 filename
[Setup] Set Screenshot Directory EMBED
${result} = Capture Fullpage Screenshot BASE64
Should Not Be Empty ${result}
Should Match Regexp ${result} ^[A-Za-z0-9+/=]+$

Capture fullpage screenshot when no browser
[Setup] Close All Browsers
${result} = Capture Fullpage Screenshot
Should Be Equal ${result} ${None}
101 changes: 101 additions & 0 deletions src/SeleniumLibrary/keywords/screenshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

DEFAULT_FILENAME_PAGE = "selenium-screenshot-{index}.png"
DEFAULT_FILENAME_ELEMENT = "selenium-element-screenshot-{index}.png"
DEFAULT_FILENAME_FULLPAGE = "selenium-fullpage-screenshot-{index}.png"
EMBED = "EMBED"
BASE64 = "BASE64"
EMBEDDED_OPTIONS = [EMBED, BASE64]
Expand Down Expand Up @@ -199,6 +200,101 @@ def _capture_element_screen_to_log(self, element, return_val):
return base64_str
return EMBED

@keyword
def capture_fullpage_screenshot(self, filename: str = DEFAULT_FILENAME_FULLPAGE) -> str:
"""Takes a screenshot of the entire page, including parts not visible in viewport.

This keyword is useful when you need to capture a long page that requires
scrolling. It works by temporarily resizing the browser window to show
the full page height, taking the screenshot, then restoring the original size.

``filename`` argument specifies where to save the screenshot file.
The directory can be set with `Set Screenshot Directory` keyword or
when importing the library. If not configured, screenshots go to the
same directory as Robot Framework's log file.

If ``filename`` is EMBED (case insensitive), the screenshot gets embedded
as Base64 image in log.html without creating a file. If it's BASE64,
the base64 string is returned and also embedded in the log.

The ``{index}`` marker in filename gets replaced with a unique number
to prevent overwriting files. You can customize the format like
``{index:03}`` for zero-padded numbers.

Returns the absolute path to the screenshot file, or EMBED/BASE64 string
if those options are used.

Examples:
| `Capture Fullpage Screenshot` | |
| `File Should Exist` | ${OUTPUTDIR}/selenium-fullpage-screenshot-1.png |
| ${path} = | `Capture Fullpage Screenshot` |
| `Capture Fullpage Screenshot` | custom_fullpage.png |
| `Capture Fullpage Screenshot` | custom_{index}.png |
| `Capture Fullpage Screenshot` | EMBED |
"""
if not self.drivers.current:
self.info("Cannot capture fullpage screenshot because no browser is open.")
return
is_embedded, method = self._decide_embedded(filename)
if is_embedded:
return self._capture_fullpage_screen_to_log(method)
return self._capture_fullpage_screenshot_to_file(filename)

def _capture_fullpage_screenshot_to_file(self, filename):
"""Save fullpage screenshot to file."""
# Remember current window size so we can restore it later
original_size = self.driver.get_window_size()

try:
# Get the actual page height - this covers all the content
full_height = self.driver.execute_script("return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);")

# Resize window to show the full page
self.driver.set_window_size(original_size['width'], full_height)

# Give the page a moment to render after resize
import time
time.sleep(0.5)

# Now take the screenshot
path = self._get_screenshot_path(filename)
self._create_directory(path)
if not self.driver.save_screenshot(path):
raise RuntimeError(f"Failed to save fullpage screenshot '{path}'.")
self._embed_to_log_as_file(path, 800)
return path

finally:
# Put the window back to its original size
self.driver.set_window_size(original_size['width'], original_size['height'])

def _capture_fullpage_screen_to_log(self, return_val):
"""Get fullpage screenshot as base64 or embed it."""
# Remember current window size so we can restore it later
original_size = self.driver.get_window_size()

try:
# Get the actual page height - this covers all the content
full_height = self.driver.execute_script("return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);")

# Resize window to show the full page
self.driver.set_window_size(original_size['width'], full_height)

# Give the page a moment to render after resize
import time
time.sleep(0.5)

# Take the screenshot as base64
screenshot_as_base64 = self.driver.get_screenshot_as_base64()
base64_str = self._embed_to_log_as_base64(screenshot_as_base64, 800)
if return_val == BASE64:
return base64_str
return EMBED

finally:
# Put the window back to its original size
self.driver.set_window_size(original_size['width'], original_size['height'])

@property
def _screenshot_root_directory(self):
return self.ctx.screenshot_root_directory
Expand All @@ -219,6 +315,11 @@ def _decide_embedded(self, filename):
and self._screenshot_root_directory in EMBEDDED_OPTIONS
):
return True, self._screenshot_root_directory
if (
filename == DEFAULT_FILENAME_FULLPAGE.upper()
and self._screenshot_root_directory in EMBEDDED_OPTIONS
):
return True, self._screenshot_root_directory
if filename in EMBEDDED_OPTIONS:
return True, self._screenshot_root_directory
return False, None
Expand Down
24 changes: 24 additions & 0 deletions utest/test/keywords/test_screen_shot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

SCREENSHOT_FILE_NAME = "selenium-screenshot-{index}.png"
ELEMENT_FILE_NAME = "selenium-element-screenshot-{index}.png"
FULLPAGE_FILE_NAME = "selenium-fullpage-screenshot-{index}.png"
EMBED = "EMBED"
BASE64 = "BASE64"

Expand Down Expand Up @@ -102,3 +103,26 @@ def test_sl_set_screenshot_directory():
cur_dir = dirname(abspath(__file__))
sl.set_screenshot_directory(cur_dir)
assert sl.screenshot_root_directory == cur_dir


def test_fullpage_defaults(screen_shot):
assert screen_shot._decide_embedded(FULLPAGE_FILE_NAME) == (False, None)


def test_fullpage_screen_shotdir_embeded(screen_shot):
screen_shot.ctx.screenshot_root_directory = EMBED
assert screen_shot._decide_embedded(FULLPAGE_FILE_NAME) == (True, EMBED)
assert screen_shot._decide_embedded(FULLPAGE_FILE_NAME.upper()) == (True, EMBED)


def test_fullpage_screen_shotdir_return_base64(screen_shot):
screen_shot.ctx.screenshot_root_directory = BASE64
assert screen_shot._decide_embedded(FULLPAGE_FILE_NAME) == (True, BASE64)
assert screen_shot._decide_embedded(FULLPAGE_FILE_NAME.upper()) == (True, BASE64)


def test_fullpage_file_name_embeded(screen_shot):
screen_shot.ctx.screenshot_root_directory = EMBED
assert screen_shot._decide_embedded(EMBED) == (True, EMBED)
screen_shot.ctx.screenshot_root_directory = BASE64
assert screen_shot._decide_embedded(BASE64) == (True, BASE64)