Starting process: bash /home/lukas/Volume/repo/Aniworld/tests/robot/start_server.sh ${handle} = <Popen: returncode: None args: ['bash', '/home/lukas/Volume/repo/Aniworld/te...> ${handle} bash /home/lukas/Volume/repo/Aniworld/tests/robot/start_server.sh cwd=/home/lukas/Volume/repo/Aniworld stdout=/home/lukas/Volume/repo/Aniworld/tests/robot/resources/../fixtures/server_stdout.log stderr=/home/lukas/Volume/repo/Aniworld/tests/robot/resources/../fixtures/server_stderr.log Starts a new process on background. ${SERVER_PROCESS} = <Popen: returncode: None args: ['bash', '/home/lukas/Volume/repo/Aniworld/te...> ${SERVER_PROCESS} ${handle} Makes a variable available everywhere within the scope of the current suite. Slept 5 seconds. 5s Pauses the test executed for the given time. Start the FastAPI uvicorn server as a background process. Creating Session using : alias=anon, url=http://127.0.0.1:8765, headers={}, cookies={}, auth=None, timeout=None, proxies=None, verify=False, debug=0 anon ${BASE_URL} Create Session: create a HTTP session to a server Create an unauthenticated HTTP session. GET Request : url=http://127.0.0.1:8765/health path_url=/health headers={'User-Agent': 'python-requests/2.33.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive'} body=None GET Response : url=http://127.0.0.1:8765/health status=200, reason=OK headers={'date': 'Fri, 26 Jun 2026 22:11:05 GMT', 'server': 'uvicorn', 'content-length': '583', 'content-type': 'application/json'} body={"status":"healthy","timestamp":"2026-06-27T00:11:05.475722","version":"v1.4.17","service":"aniworld-api","series_app_initialized":true,"anime_directory_configured":true,"scheduler_next_run":"2026-06-27T03:00:00+02:00","scheduler_last_run":null,"checks":{"ffmpeg":{"status":"ok","message":"Found at /usr/bin/ffmpeg"},"dns_aniworld":{"status":"ok","message":"Resolved successfully"},"dns_tmdb":{"status":"ok","message":"Resolved successfully"},"anime_directory":{"status":"ok","message":"Directory exists and is writable: /tmp/aniworld_test_anime","path":"/tmp/aniworld_test_anime"}}} anon /health expected_status=any Sends a GET request on a previously created HTTP Session. ${resp} = ('PASS', <Response [200]>) ${resp} GET On Session anon /health expected_status=any Runs the given keyword with the given arguments and ignores possible error. ${status_code} = 200 ${status_code} $resp[1].status_code Evaluates the given expression in Python and returns the result. 1s Pauses the test executed for the given time. 0 ${i} 30 Server did not become healthy within 30 seconds Fails the test with the given message and optionally alters its tags. Poll the health endpoint until the server responds. Accepts 200 or 503 (not configured yet) as valid responses. Retries every 1 second for up to 30 seconds. ${payload} = {'master_password': 'TestPass123!', 'anime_directory': '/tmp/aniworld_test_anime', 'name': 'AniworldTest', 'data_dir': 'data', 'scheduler_enabled': 'False', 'logging_level': 'INFO', 'backup_enabled': ... ${payload} master_password=${SETUP_PASSWORD} anime_directory=/tmp/aniworld_test_anime name=AniworldTest data_dir=data scheduler_enabled=False logging_level=INFO backup_enabled=False Creates and returns a dictionary based on the given ``items``. POST Request : url=http://127.0.0.1:8765/api/auth/setup path_url=/api/auth/setup headers={'User-Agent': 'python-requests/2.33.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '208', 'Content-Type': 'application/json'} body=b'{"master_password": "TestPass123!", "anime_directory": "/tmp/aniworld_test_anime", "name": "AniworldTest", "data_dir": "data", "scheduler_enabled": "False", "logging_level": "INFO", "backup_enabled": "False"}' POST Response : url=http://127.0.0.1:8765/api/auth/setup status=400, reason=Bad Request headers={'date': 'Fri, 26 Jun 2026 22:11:05 GMT', 'server': 'uvicorn', 'content-length': '47', 'content-type': 'application/json'} body={"detail":"Master password already configured"} ${resp} = <Response [400]> ${resp} anon /api/auth/setup json=${payload} expected_status=any Sends a POST request on a previously created HTTP Session. 45s Pauses the test executed for the given time. ${resp} anon /api/auth/setup json=${payload} expected_status=any Sends a POST request on a previously created HTTP Session. 45s Pauses the test executed for the given time. ${resp} anon /api/auth/setup json=${payload} expected_status=any Sends a POST request on a previously created HTTP Session. ${json} = {'detail': 'Master password already configured'} ${json} ${resp.text} Convert String to JSON object ${detail} = ['Master password already configured'] ${detail} ${json} $.detail Get Value From JSON using JSONPath Setup failed with 400: ${detail} Fails the test with the given message and optionally alters its tags. ${resp.status_code} 201 Fails if objects are unequal after converting them to integers. Unexpected status ${resp.status_code} from /api/auth/setup Fails the test with the given message and optionally alters its tags. Configure the master password via the setup endpoint. ${payload} = {'password': 'TestPass123!'} ${payload} password=${SETUP_PASSWORD} Creates and returns a dictionary based on the given ``items``. POST Request : url=http://127.0.0.1:8765/api/auth/login path_url=/api/auth/login headers={'User-Agent': 'python-requests/2.33.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '28', 'Content-Type': 'application/json'} body=b'{"password": "TestPass123!"}' POST Response : url=http://127.0.0.1:8765/api/auth/login status=200, reason=OK headers={'date': 'Fri, 26 Jun 2026 22:11:05 GMT', 'server': 'uvicorn', 'content-length': '232', 'content-type': 'application/json'} body={"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY","token_type":"bearer","expires_at":"2026-06-27T22:11:05.521518Z"} ${resp} = <Response [200]> ${resp} anon /api/auth/login json=${payload} expected_status=any Sends a POST request on a previously created HTTP Session. 6s Pauses the test executed for the given time. ${resp} anon /api/auth/login json=${payload} expected_status=any Sends a POST request on a previously created HTTP Session. ${json} ${resp.text} Convert String to JSON object ${detail} ${json} $.detail Get Value From JSON using JSONPath ${detail_val} ${detail}[0] Returns the given values which can then be assigned to a variables. Login failed with ${resp.status_code}: ${detail_val} Fails the test with the given message and optionally alters its tags. ${json} = {'access_token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY', 'token_type': 'bearer', 'expire... ${json} ${resp.text} Convert String to JSON object ${token} = ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY'] ${token} ${json} $.access_token Get Value From JSON using JSONPath ${token_str} = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY ${token_str} ${token}[0] Returns the given values which can then be assigned to a variables. ${TOKEN} = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY ${TOKEN} ${token_str} Makes a variable available everywhere within the scope of the current suite. ${token_str} Log in with the master password and return the JWT access token. Creating Session using : alias=auth, url=http://127.0.0.1:8765, headers={'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYXN0ZXIiLCJleHAiOjE3ODI1OTgyNjUsImlhdCI6MTc4MjUxMTg2NX0.oQ307cnSsVWuTlBbwlWWrNTrbSD4kK2o9uqnC2s1KFY'}, cookies={}, auth=None, timeout=None, proxies=None, verify=False, debug=0 auth ${BASE_URL} headers={'Authorization': 'Bearer ${TOKEN}'} Create Session: create a HTTP session to a server {"browser": "chromium", "headless": true, "chromiumSandbox": false, "devtools": false, "handleSIGHUP": true, "handleSIGINT": true, "handleSIGTERM": true, "slowMo": 0.0, "timeout": 30000.0, "tracesDir": "/home/lukas/Volume/repo/Aniworld/robot_output/browser/traces/temp/f808209d-8694-49b8-8298-841cb668b8c1"} Starting Browser process /home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js using at 127.0.0.1:48397 Successfully created browser with options: {"browser":"chromium","headless":true,"chromiumSandbox":false,"devtools":false,"handleSIGHUP":true,"handleSIGINT":true,"handleSIGTERM":true,"slowMo":0,"timeout":30000,"tracesDir":"/home/lukas/Volume/repo/Aniworld/robot_output/browser/traces/temp/f808209d-8694-49b8-8298-841cb668b8c1"} ${BROWSER} headless=${HEADLESS} BrowserControl Setter Create a new playwright Browser with specified options. { "acceptDownloads": true, "bypassCSP": false, "forcedColors": "none", "ignoreHTTPSErrors": false, "javaScriptEnabled": true, "offline": false, "reducedMotion": "no-preference", "serviceWorkers": "allow", "viewport": { "width": 1280, "height": 720 } } Successfully created context. {'acceptDownloads': True, 'bypassCSP': False, 'forcedColors': 'none', 'ignoreHTTPSErrors': False, 'javaScriptEnabled': True, 'offline': False, 'reducedMotion': 'no-preference', 'serviceWorkers': 'allow', 'viewport': {'width': 1280, 'height': 720}} viewport={'width': 1280, 'height': 720} BrowserControl Setter Create a new BrowserContext with specified options. Initialize Browser library with the configured browser and headless mode. Start Aniworld Server AND Create Anonymous Session AND Wait For Server AND Setup Master Password AND Login And Get Token AND Create Session auth ${BASE_URL} headers={'Authorization': 'Bearer ${TOKEN}'} AND Initialize Browser Executes all the given keywords in a sequence. Successfully initialized new page object and opened url: http://127.0.0.1:8765/login ${BASE_URL}${path} BrowserControl Setter Open a new Page. /login Open a new page at the given path relative to BASE_URL. Fills the text 'TestPass123!' in the given field. id=password-input ${password} PageContent Setter Clears and fills the given ``txt`` into the text field found by ``selector``. Clicks the element 'id=login-submit-btn'. id=login-submit-btn PageContent Setter Simulates mouse click on the element found by ``selector``. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Fill the login form and submit it. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Open Browser To Page /login AND Perform Login AND Wait For Elements State id=search-input visible timeout=5s Executes all the given keywords in a sequence. Set viewport size to: {"width":375,"height":667} 375 667 BrowserControl Setter Sets current Pages viewport size to specified dimensions. Resize the browser viewport to mobile dimensions. Reloaded page with options: {"waitUntil": "load"} BrowserControl Setter Reloads current active page. Reload the current page. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Waited for Element with selector id=search-input at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=search-input Assert that the element matching the selector is visible. Waited for Element with selector id=theme-toggle at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=theme-toggle Assert that the element matching the selector is visible. Closed all browsers ALL BrowserControl Setter Closes the current browser. Close all browser contexts and the browser itself. Resize to mobile and verify layout adapts. Successfully initialized new page object and opened url: http://127.0.0.1:8765/login No browser and context was open. New browser and context was automatically opened when page is created. ${BASE_URL}${path} BrowserControl Setter Open a new Page. /login Open a new page at the given path relative to BASE_URL. Fills the text 'TestPass123!' in the given field. id=password-input ${password} PageContent Setter Clears and fills the given ``txt`` into the text field found by ``selector``. Clicks the element 'id=login-submit-btn'. id=login-submit-btn PageContent Setter Simulates mouse click on the element found by ``selector``. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Fill the login form and submit it. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Open Browser To Page /login AND Perform Login AND Wait For Elements State id=search-input visible timeout=5s Executes all the given keywords in a sequence. Set viewport size to: {"width":375,"height":667} 375 667 BrowserControl Setter Sets current Pages viewport size to specified dimensions. Resize the browser viewport to mobile dimensions. Reloaded page with options: {"waitUntil": "load"} BrowserControl Setter Reloads current active page. Reload the current page. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Waited for Element with selector id=theme-toggle at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=theme-toggle Assert that the element matching the selector is visible. Closed all browsers ALL BrowserControl Setter Closes the current browser. Close all browser contexts and the browser itself. Verify compact header on mobile. Successfully initialized new page object and opened url: http://127.0.0.1:8765/login No browser and context was open. New browser and context was automatically opened when page is created. ${BASE_URL}${path} BrowserControl Setter Open a new Page. /login Open a new page at the given path relative to BASE_URL. Fills the text 'TestPass123!' in the given field. id=password-input ${password} PageContent Setter Clears and fills the given ``txt`` into the text field found by ``selector``. Clicks the element 'id=login-submit-btn'. id=login-submit-btn PageContent Setter Simulates mouse click on the element found by ``selector``. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Fill the login form and submit it. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Open Browser To Page /login AND Perform Login AND Wait For Elements State id=search-input visible timeout=5s Executes all the given keywords in a sequence. Set viewport size to: {"width":768,"height":1024} 768 1024 BrowserControl Setter Sets current Pages viewport size to specified dimensions. Resize the browser viewport to tablet dimensions. Reloaded page with options: {"waitUntil": "load"} BrowserControl Setter Reloads current active page. Reload the current page. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Waited for Element with selector id=search-input at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=search-input Assert that the element matching the selector is visible. Waited for Element with selector id=rescan-btn at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=rescan-btn Assert that the element matching the selector is visible. Closed all browsers ALL BrowserControl Setter Closes the current browser. Close all browser contexts and the browser itself. Resize to tablet and verify layout adapts. Successfully initialized new page object and opened url: http://127.0.0.1:8765/login No browser and context was open. New browser and context was automatically opened when page is created. ${BASE_URL}${path} BrowserControl Setter Open a new Page. /login Open a new page at the given path relative to BASE_URL. Fills the text 'TestPass123!' in the given field. id=password-input ${password} PageContent Setter Clears and fills the given ``txt`` into the text field found by ``selector``. Clicks the element 'id=login-submit-btn'. id=login-submit-btn PageContent Setter Simulates mouse click on the element found by ``selector``. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Fill the login form and submit it. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Open Browser To Page /login AND Perform Login AND Wait For Elements State id=search-input visible timeout=5s Executes all the given keywords in a sequence. Set viewport size to: {"width":375,"height":667} 375 667 BrowserControl Setter Sets current Pages viewport size to specified dimensions. Resize the browser viewport to mobile dimensions. Reloaded page with options: {"waitUntil": "load"} BrowserControl Setter Reloads current active page. Reload the current page. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Clicks the element 'id=theme-toggle'. id=theme-toggle PageContent Setter Simulates mouse click on the element found by ``selector``. Attribute is: 'dark' ${theme} = dark ${theme} html data-theme Assertion Getter PageContent Returns the HTML ``attribute`` of the element found by ``selector``. ${theme} ${theme} = dark ${theme} Return the current theme attribute from the HTML element. ${theme} dark Fails if objects are unequal after converting them to strings. Clicks the element 'id=theme-toggle'. id=theme-toggle PageContent Setter Simulates mouse click on the element found by ``selector``. Attribute is: 'light' ${theme} = light ${theme} html data-theme Assertion Getter PageContent Returns the HTML ``attribute`` of the element found by ``selector``. ${theme} ${theme} = light ${theme} Return the current theme attribute from the HTML element. ${theme} light Fails if objects are unequal after converting them to strings. Closed all browsers ALL BrowserControl Setter Closes the current browser. Close all browser contexts and the browser itself. Verify buttons remain clickable on touch-sized viewport. Successfully initialized new page object and opened url: http://127.0.0.1:8765/login No browser and context was open. New browser and context was automatically opened when page is created. ${BASE_URL}${path} BrowserControl Setter Open a new Page. /login Open a new page at the given path relative to BASE_URL. Fills the text 'TestPass123!' in the given field. id=password-input ${password} PageContent Setter Clears and fills the given ``txt`` into the text field found by ``selector``. Clicks the element 'id=login-submit-btn'. id=login-submit-btn PageContent Setter Simulates mouse click on the element found by ``selector``. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Fill the login form and submit it. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Open Browser To Page /login AND Perform Login AND Wait For Elements State id=search-input visible timeout=5s Executes all the given keywords in a sequence. Set viewport size to: {"width":1280,"height":720} 1280 720 BrowserControl Setter Sets current Pages viewport size to specified dimensions. Resize the browser viewport to desktop dimensions. Reloaded page with options: {"waitUntil": "load"} BrowserControl Setter Reloads current active page. Reload the current page. Waited for Element with selector id=search-input at state visible id=search-input visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. Waited for Element with selector id=search-input at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=search-input Assert that the element matching the selector is visible. Waited for Element with selector id=rescan-btn at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=rescan-btn Assert that the element matching the selector is visible. Waited for Element with selector id=config-btn at state visible ${selector} visible timeout=5s PageContent Wait Waits for the element found by ``selector`` to satisfy state option. id=config-btn Assert that the element matching the selector is visible. Closed all browsers ALL BrowserControl Setter Closes the current browser. Close all browser contexts and the browser itself. Resize back to desktop and verify normal layout. Responsive design UI tests for Aniworld. Covers mobile and tablet viewport layouts and touch interactions. All Tests Responsive