diff --git a/Docs/tasks.md b/Docs/tasks.md
index 8cbe642..fdc0ed4 100644
--- a/Docs/tasks.md
+++ b/Docs/tasks.md
@@ -1,19 +1,3 @@
-### Task 17: Fix `Login With Valid Password` UI Test
-**Test Result:** FAIL — Same as Task 16 (`id=password-input` not found)
-**File:** `tests/robot/ui/login.robot` and `src/server/web/templates/login.html`
-**Instructions:**
-Fix the element ID mismatches in `login.html` as described in Task 16. The `Perform Login` keyword in `tests/robot/resources/ui_keywords.resource` fills `id=password-input` and clicks `id=login-submit-btn`. Update the HTML template IDs to match.
-
----
-
-### Task 18: Fix `Login With Invalid Password` UI Test
-**Test Result:** FAIL — Same as Task 16
-**File:** `tests/robot/ui/login.robot` and `src/server/web/templates/login.html`
-**Instructions:**
-Fix the element ID mismatches in `login.html` as described in Task 16. Also verify that the error message element `id=login-error` exists in the template and is shown when login fails.
-
----
-
### Task 19: Fix `Login Rate Limit UI` UI Test
**Test Result:** FAIL — Same as Task 16
**File:** `tests/robot/ui/login.robot` and `src/server/web/templates/login.html`
diff --git a/log.html b/log.html
new file mode 100644
index 0000000..4fefd19
--- /dev/null
+++ b/log.html
@@ -0,0 +1,2467 @@
+
+
+
Verify that you have JavaScript enabled in your browser.
+
Make sure you are using a modern enough browser. If using Internet Explorer, version 11 is required.
+
Check are there messages in your browser's JavaScript error log. Please report the problem if you suspect you have encountered a bug.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/output.xml b/output.xml
new file mode 100644
index 0000000..8647a7c
--- /dev/null
+++ b/output.xml
@@ -0,0 +1,676 @@
+
+
+
+
+
+
+
+anon
+${BASE_URL}
+Create Session: create a HTTP session to a server
+
+
+Create an unauthenticated HTTP session.
+
+
+
+
+${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``.
+
+
+
+${resp}
+anon
+/api/auth/setup
+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/setup
+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
+
+
+
+
+
+
+
+
+
+
+
+
+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.
+
+
+
+
+
+anon
+${BASE_URL}
+Create Session: create a HTTP session to a server
+
+
+Create an unauthenticated HTTP session.
+
+
+
+
+${resp}
+anon
+/api/auth/status
+expected_status=200
+Sends a GET request on a previously created HTTP Session.
+
+
+
+${json}
+${resp.text}
+Convert String to JSON object
+
+
+
+${configured}
+${json}
+$.configured
+Get Value From JSON using JSONPath
+
+
+
+${configured_val}
+${configured}[0]
+Returns the given values which can then be assigned to a variables.
+
+
+
+${configured_val}
+
+
+${configured}
+Check if the master password has already been configured.
+
+
+
+
+
+
+${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``.
+
+
+
+${resp}
+anon
+/api/auth/setup
+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/setup
+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
+
+
+
+
+
+
+
+
+
+
+
+
+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=${SETUP_PASSWORD}
+Creates and returns a dictionary based on the given ``items``.
+
+
+
+${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}
+${resp.text}
+Convert String to JSON object
+
+
+
+${token}
+${json}
+$.access_token
+Get Value From JSON using JSONPath
+
+
+
+${token_str}
+${token}[0]
+Returns the given values which can then be assigned to a variables.
+
+
+
+${TOKEN}
+${token_str}
+Makes a variable available everywhere within the scope of the current suite.
+
+
+
+${token_str}
+
+
+${token}
+Log in with the master password and return the JWT access token.
+
+
+
+auth
+${BASE_URL}
+headers={'Authorization': 'Bearer ${token}'}
+Create Session: create a HTTP session to a server
+
+
+
+
+
+
+
+${payload}
+password=${SETUP_PASSWORD}
+Creates and returns a dictionary based on the given ``items``.
+
+
+
+${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}
+${resp.text}
+Convert String to JSON object
+
+
+
+${token}
+${json}
+$.access_token
+Get Value From JSON using JSONPath
+
+
+
+${token_str}
+${token}[0]
+Returns the given values which can then be assigned to a variables.
+
+
+
+${TOKEN}
+${token_str}
+Makes a variable available everywhere within the scope of the current suite.
+
+
+
+${token_str}
+
+
+${token}
+Log in with the master password and return the JWT access token.
+
+
+
+auth
+${BASE_URL}
+headers={'Authorization': 'Bearer ${token}'}
+Create Session: create a HTTP session to a server
+
+
+
+
+
+
+auth
+${BASE_URL}
+headers={'Authorization': 'Bearer ${TOKEN}'}
+Create Session: create a HTTP session to a server
+
+
+
+
+
+
+Create an authenticated HTTP session. Performs setup if needed, then logs in.
+Skips setup/login if already authenticated (suite-level setup done).
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+AND
+Create Authenticated Session
+Executes all the given keywords in a sequence.
+
+
+
+${episodes}
+{'season': 1, 'episode': 1, 'title': 'Episode 1'}
+Returns a list containing given items.
+
+
+
+${payload}
+serie_id=retry-test
+serie_folder=Retry Test
+serie_name=Retry Test
+episodes=${episodes}
+priority=NORMAL
+Creates and returns a dictionary based on the given ``items``.
+
+
+
+
+
+
+
+${resp}
+${session}
+${endpoint}
+expected_status=${expected_status}
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+
+${resp}
+${session}
+${endpoint}
+json=${payload}
+expected_status=${expected_status}
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+
+
+${resp}
+
+
+${resp}
+/api/queue/add
+${payload}
+201
+Perform an authenticated POST request with optional JSON payload.
+
+
+
+${resp}
+
+
+${add_resp}
+${payload}
+POST /api/queue/add with the given payload.
+
+
+
+${json}
+${add_resp.text}
+Convert String to JSON object
+
+
+
+${ids}
+${json}
+$.item_ids
+Get Value From JSON using JSONPath
+
+
+
+${item_id}
+${ids}[0]
+Returns the given values which can then be assigned to a variables.
+
+
+
+${retry_payload}
+item_ids=${item_id}
+Creates and returns a dictionary based on the given ``items``.
+
+
+
+
+
+
+${resp}
+${session}
+${endpoint}
+expected_status=${expected_status}
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+
+${resp}
+${session}
+${endpoint}
+json=${payload}
+expected_status=${expected_status}
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+
+
+${resp}
+
+
+${retry_resp}
+/api/queue/retry
+${retry_payload}
+Perform an authenticated POST request with optional JSON payload.
+
+
+
+
+${response.status_code}
+${expected_status}
+Fails if objects are unequal after converting them to integers.
+
+
+${retry_resp}
+200
+Assert that the response status code matches the expected value.
+
+
+
+${retry_json}
+${retry_resp.text}
+Convert String to JSON object
+
+
+
+${retry_json}
+retried_count
+Fails if ``key`` is not found from ``dictionary``.
+
+
+
+Removes all the session objects
+
+
+Starting Browser process /home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js using at 127.0.0.1:35183
+Retry a failed download item.
+
+
+Download queue API tests for Aniworld.
+Covers status, add, start/stop/pause/resume, clear, retry, and remove.
+
+
+
+
+All Tests
+
+
+
+
+Download
+
+
+
+
+
diff --git a/playwright-log.txt b/playwright-log.txt
new file mode 100644
index 0000000..3d22a0d
--- /dev/null
+++ b/playwright-log.txt
@@ -0,0 +1,9 @@
+{"level":"info","time":"2026-06-26T16:51:25.695Z","seq":1,"component":"browser-library","msg":"Listening on 127.0.0.1:35183"}
+{"level":"error","time":"2026-06-26T16:51:25.731Z","seq":2,"component":"browser-library","event_kind":"grpc_error","status":"failed","error_type":"Error","msg":"Error: Browser has been closed.\n at PlaywrightState.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:12594:13)\n at PlaywrightServer.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13516:52)\n at PlaywrightServer.setTimeout (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13734:56)\n at Object.onReceiveHalfClose (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server.js:1478:25)\n at BaseServerInterceptingCall.maybePushNextMessage (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:594:31)\n at BaseServerInterceptingCall.handleEndEvent (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:634:14)\n at ServerHttp2Stream. (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:387:18)\n at ServerHttp2Stream.emit (node:events:531:35)\n at endReadableNT (node:internal/streams/readable:1698:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)"}
+{"level":"info","time":"2026-06-26T16:51:25.738Z","seq":3,"suite_id":"s1","suite_name":"Download","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T16:51:25.739Z","seq":4,"suite_id":"s1","suite_name":"Download","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"error","time":"2026-06-26T16:51:25.741Z","seq":5,"suite_id":"s1","suite_name":"Download","component":"browser-library","event_kind":"grpc_error","status":"failed","error_type":"Error","msg":"Error: Browser has been closed.\n at PlaywrightState.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:12594:13)\n at PlaywrightServer.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13516:52)\n at PlaywrightServer.setTimeout (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13734:56)\n at Object.onReceiveHalfClose (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server.js:1478:25)\n at BaseServerInterceptingCall.maybePushNextMessage (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:594:31)\n at BaseServerInterceptingCall.handleEndEvent (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:634:14)\n at ServerHttp2Stream. (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:387:18)\n at ServerHttp2Stream.emit (node:events:531:35)\n at endReadableNT (node:internal/streams/readable:1698:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)"}
+{"level":"info","time":"2026-06-26T16:51:25.746Z","seq":6,"component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T16:51:25.747Z","seq":7,"component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"info","time":"2026-06-26T16:51:25.784Z","seq":8,"component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"started"}
+{"level":"info","time":"2026-06-26T16:51:25.784Z","seq":9,"component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"succeeded"}
diff --git a/report.html b/report.html
new file mode 100644
index 0000000..4c5a384
--- /dev/null
+++ b/report.html
@@ -0,0 +1,2729 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Opening Robot Framework report failed
+
+
Verify that you have JavaScript enabled in your browser.
+
Make sure you are using a modern enough browser. If using Internet Explorer, version 11 is required.
+
Check are there messages in your browser's JavaScript error log. Please report the problem if you suspect you have encountered a bug.
Verify that you have JavaScript enabled in your browser.
+
Make sure you are using a modern enough browser. If using Internet Explorer, version 11 is required.
+
Check are there messages in your browser's JavaScript error log. Please report the problem if you suspect you have encountered a bug.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/robot_output/output.xml b/robot_output/output.xml
new file mode 100644
index 0000000..0975527
--- /dev/null
+++ b/robot_output/output.xml
@@ -0,0 +1,1679 @@
+
+
+
+
+
+
+
+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.
+
+
+
+
+${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=429, reason=Too Many Requests
+ headers={'date': 'Fri, 26 Jun 2026 19:00:40 GMT', 'server': 'uvicorn', 'content-length': '74', 'content-type': 'application/json'}
+ body={"detail":"Too many authentication attempts, try again later. IP lockout"}
+
+${resp} = <Response [429]>
+${resp}
+anon
+/api/auth/setup
+json=${payload}
+expected_status=any
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+Slept 45 seconds.
+45s
+Pauses the test executed for the given time.
+
+
+
+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 19:01:25 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.
+
+
+
+
+
+
+
+
+
+${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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+Starting Browser process /home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js using at 127.0.0.1:40597
+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.
+
+
+
+
+Title: 'AniWorld Manager - Login'
+${title} = AniWorld Manager - Login
+${title}
+Assertion
+Getter
+PageContent
+Returns the title of the current page.
+
+
+
+${title}
+${text}
+Fails if ``container`` does not contain ``item`` one or more times.
+
+
+Login
+Assert that the page title contains the given text.
+
+
+
+
+Waited for Element with selector id=password-input at state visible
+${selector}
+visible
+timeout=5s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+id=password-input
+Assert that the element matching the selector is visible.
+
+
+
+
+Waited for Element with selector id=login-submit-btn at state visible
+${selector}
+visible
+timeout=5s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+id=login-submit-btn
+Assert that the element matching the selector is visible.
+
+
+
+
+Waited for Element with selector id=password-toggle at state visible
+${selector}
+visible
+timeout=5s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+id=password-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 the login page loads with all form elements.
+
+
+
+
+
+
+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.
+
+
+
+
+${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 19:01:26 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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+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.
+
+
+${SETUP_PASSWORD}
+Fill the login form and submit it.
+
+
+
+
+Title: 'AniWorld Manager'
+${title} = AniWorld Manager
+${title}
+Assertion
+Getter
+PageContent
+Returns the title of the current page.
+
+
+
+${title}
+${text}
+Fails if ``container`` does not contain ``item`` one or more times.
+
+
+AniWorld
+Assert that the page title contains the given text.
+
+
+
+
+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.
+
+
+
+
+Closed all browsers
+ALL
+BrowserControl
+Setter
+Closes the current browser.
+
+
+Close all browser contexts and the browser itself.
+
+
+Log in with the correct password and verify redirect to dashboard.
+
+
+
+
+
+
+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.
+
+
+
+
+${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 19:01:28 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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+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 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+${selector}
+visible
+timeout=5s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+id=login-error
+Assert that the element matching the selector is visible.
+
+
+
+
+Text: ['invalid credentials']
+${element_text} = invalid credentials
+${element_text}
+${selector}
+Assertion
+Getter
+PageContent
+Returns text attribute of the element found by ``selector``.
+
+
+
+${element_text}
+${text}
+Fails if ``container`` does not contain ``item`` one or more times.
+
+
+id=login-error
+invalid
+Assert that the element's text contains the given substring.
+
+
+
+
+Closed all browsers
+ALL
+BrowserControl
+Setter
+Closes the current browser.
+
+
+Close all browser contexts and the browser itself.
+
+
+Log in with an incorrect password and verify error message.
+
+
+
+
+
+
+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.
+
+
+
+
+${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 19:01:28 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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+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 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+0
+
+
+
+
+Fills the text 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+1
+
+
+
+
+Fills the text 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+2
+
+
+
+
+Fills the text 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+3
+
+
+
+
+Fills the text 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+4
+
+
+
+
+Fills the text 'WrongPass123!' in the given field.
+id=password-input
+WrongPass123!
+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=login-error at state visible
+id=login-error
+visible
+timeout=3s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+
+
+5
+
+
+${i}
+6
+
+
+
+
+Text: ['Too many authentication attempts, try again later. IP lockout']
+${element_text} = Too many authentication attempts, try again later. IP lockout
+${element_text}
+${selector}
+Assertion
+Getter
+PageContent
+Returns text attribute of the element found by ``selector``.
+
+
+
+${element_text}
+${text}
+Fails if ``container`` does not contain ``item`` one or more times.
+
+
+id=login-error
+lockout
+Assert that the element's text contains the given substring.
+
+
+
+
+Closed all browsers
+ALL
+BrowserControl
+Setter
+Closes the current browser.
+
+
+Close all browser contexts and the browser itself.
+
+
+Submit multiple failed logins and verify lockout message appears.
+
+
+
+
+
+
+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.
+
+
+
+
+${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=429, reason=Too Many Requests
+ headers={'date': 'Fri, 26 Jun 2026 19:01:30 GMT', 'server': 'uvicorn', 'content-length': '74', 'content-type': 'application/json'}
+ body={"detail":"Too many authentication attempts, try again later. IP lockout"}
+
+${resp} = <Response [429]>
+${resp}
+anon
+/api/auth/setup
+json=${payload}
+expected_status=any
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+Slept 45 seconds.
+45s
+Pauses the test executed for the given time.
+
+
+
+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=429, reason=Too Many Requests
+ headers={'date': 'Fri, 26 Jun 2026 19:02:15 GMT', 'server': 'uvicorn', 'content-length': '74', 'content-type': 'application/json'}
+ body={"detail":"Too many authentication attempts, try again later. IP lockout"}
+
+${resp} = <Response [429]>
+${resp}
+anon
+/api/auth/setup
+json=${payload}
+expected_status=any
+Sends a POST request on a previously created HTTP Session.
+
+
+
+
+
+
+
+
+
+Slept 45 seconds.
+45s
+Pauses the test executed for the given time.
+
+
+
+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 19:03:00 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.
+
+
+
+
+
+
+
+
+
+${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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+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
+${SETUP_PASSWORD}
+PageContent
+Setter
+Clears and fills the given ``txt`` into the text field found by ``selector``.
+
+
+
+Attribute is: 'password'
+${type_before} = password
+${type_before}
+id=password-input
+type
+Assertion
+Getter
+PageContent
+Returns the HTML ``attribute`` of the element found by ``selector``.
+
+
+
+${type_before}
+password
+Fails if objects are unequal after converting them to strings.
+
+
+
+Clicks the element 'id=password-toggle'.
+id=password-toggle
+PageContent
+Setter
+Simulates mouse click on the element found by ``selector``.
+
+
+
+Attribute is: 'text'
+${type_after} = text
+${type_after}
+id=password-input
+type
+Assertion
+Getter
+PageContent
+Returns the HTML ``attribute`` of the element found by ``selector``.
+
+
+
+${type_after}
+text
+Fails if objects are unequal after converting them to strings.
+
+
+
+Clicks the element 'id=password-toggle'.
+id=password-toggle
+PageContent
+Setter
+Simulates mouse click on the element found by ``selector``.
+
+
+
+Attribute is: 'password'
+${type_final} = password
+${type_final}
+id=password-input
+type
+Assertion
+Getter
+PageContent
+Returns the HTML ``attribute`` of the element found by ``selector``.
+
+
+
+${type_final}
+password
+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.
+
+
+Click the eye icon and verify the password field type toggles.
+
+
+
+
+
+
+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.
+
+
+
+
+${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 19:03:00 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.
+
+
+Create Anonymous Session
+AND
+Setup Master Password
+Executes all the given keywords in a sequence.
+
+
+
+
+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``.
+
+
+
+</td></tr><tr><td colspan="3"><a href="browser/screenshot/fail-screenshot-1.png" target="_blank"><img src="browser/screenshot/fail-screenshot-1.png" width="800px"/></a>
+See also file:///home/lukas/Volume/repo/Aniworld/robot_output/playwright-log.txt for additional details.
+TimeoutError: locator.waitFor: Timeout 5000ms exceeded.
+Call log:
+ - waiting for locator('id=search-input') to be visible
+
+id=search-input
+visible
+timeout=5s
+PageContent
+Wait
+Waits for the element found by ``selector`` to satisfy state option.
+TimeoutError: locator.waitFor: Timeout 5000ms exceeded.
+Call log:
+ - waiting for locator('id=search-input') to be visible
+
+
+${SETUP_PASSWORD}
+Fill the login form and submit it.
+TimeoutError: locator.waitFor: Timeout 5000ms exceeded.
+Call log:
+ - waiting for locator('id=search-input') to be visible
+
+
+
+Click the logout button and wait for redirect to login.
+
+
+
+Login
+Assert that the page title contains the given text.
+
+
+
+id=password-input
+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.
+
+
+Log in then log out and verify redirect to login page.
+TimeoutError: locator.waitFor: Timeout 5000ms exceeded.
+Call log:
+ - waiting for locator('id=search-input') to be visible
+
+
+Login UI tests for Aniworld.
+Covers login page load, valid/invalid login, rate limit UI, password visibility, and logout.
+
+
+
+
+All Tests
+
+
+
+
+Login
+
+
+
+
+
diff --git a/robot_output/playwright-log.txt b/robot_output/playwright-log.txt
new file mode 100644
index 0000000..e413eb6
--- /dev/null
+++ b/robot_output/playwright-log.txt
@@ -0,0 +1,226 @@
+{"level":"info","time":"2026-06-26T19:01:26.202Z","seq":1,"component":"browser-library","msg":"Listening on 127.0.0.1:40597"}
+{"level":"info","time":"2026-06-26T19:01:26.263Z","seq":2,"component":"browser-library","event_kind":"grpc","action":"newPage","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.264Z","seq":3,"component":"browser-library","msg":"No active browser, creating a new one"}
+{"level":"info","time":"2026-06-26T19:01:26.318Z","seq":4,"component":"browser-library","msg":"Setting default timeout for context context=d175c6c3-4b1d-4b0b-a765-ea73098025fc to 10000"}
+{"level":"info","time":"2026-06-26T19:01:26.319Z","seq":5,"component":"browser-library","msg":"Changed active context: context=d175c6c3-4b1d-4b0b-a765-ea73098025fc"}
+{"level":"info","time":"2026-06-26T19:01:26.361Z","seq":6,"component":"browser-library","msg":"Video path: undefined"}
+{"level":"info","time":"2026-06-26T19:01:26.361Z","seq":7,"component":"browser-library","msg":"Changed active page"}
+{"level":"info","time":"2026-06-26T19:01:26.651Z","seq":8,"component":"browser-library","event_kind":"grpc","action":"newPage","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.663Z","seq":9,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.664Z","seq":10,"component":"browser-library","msg":"Strict mode is enabled, find Locator with id=password-input in page."}
+{"level":"info","time":"2026-06-26T19:01:26.682Z","seq":11,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.685Z","seq":12,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.686Z","seq":13,"component":"browser-library","msg":"Strict mode is enabled, find Locator with id=login-submit-btn in page."}
+{"level":"info","time":"2026-06-26T19:01:26.689Z","seq":14,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.692Z","seq":15,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.692Z","seq":16,"component":"browser-library","msg":"Strict mode is enabled, find Locator with id=password-toggle in page."}
+{"level":"info","time":"2026-06-26T19:01:26.695Z","seq":17,"component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.698Z","seq":18,"component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.701Z","seq":19,"component":"browser-library","msg":"Removed page=09f7a0ce-587a-4766-8f97-62ed642a973b from context=d175c6c3-4b1d-4b0b-a765-ea73098025fc page stack"}
+{"level":"info","time":"2026-06-26T19:01:26.717Z","seq":20,"component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"succeeded"}
+{"level":"error","time":"2026-06-26T19:01:26.719Z","seq":21,"component":"browser-library","event_kind":"grpc_error","status":"failed","error_type":"Error","msg":"Error: Browser has been closed.\n at PlaywrightState.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:12594:13)\n at PlaywrightServer.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13516:52)\n at PlaywrightServer.setTimeout (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13734:56)\n at Object.onReceiveHalfClose (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server.js:1478:25)\n at BaseServerInterceptingCall.maybePushNextMessage (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:594:31)\n at BaseServerInterceptingCall.handleEndEvent (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:634:14)\n at ServerHttp2Stream. (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:387:18)\n at ServerHttp2Stream.emit (node:events:531:35)\n at endReadableNT (node:internal/streams/readable:1698:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)"}
+{"level":"info","time":"2026-06-26T19:01:26.723Z","seq":22,"suite_id":"s1","suite_name":"Login","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.723Z","seq":23,"suite_id":"s1","suite_name":"Login","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.726Z","seq":24,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.726Z","seq":25,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:26.747Z","seq":26,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"newPage","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:26.747Z","seq":27,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"No active browser, creating a new one"}
+{"level":"info","time":"2026-06-26T19:01:26.789Z","seq":28,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Setting default timeout for context context=39202e9e-49fd-43c8-ace8-aefc1f8fc86a to 10000"}
+{"level":"info","time":"2026-06-26T19:01:26.789Z","seq":29,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Changed active context: context=39202e9e-49fd-43c8-ace8-aefc1f8fc86a"}
+{"level":"info","time":"2026-06-26T19:01:26.817Z","seq":30,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Video path: undefined"}
+{"level":"info","time":"2026-06-26T19:01:26.817Z","seq":31,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Changed active page"}
+{"level":"info","time":"2026-06-26T19:01:27.135Z","seq":32,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"newPage","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:27.138Z","seq":33,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"fillText","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:27.138Z","seq":34,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=password-input in page."}
+{"level":"info","time":"2026-06-26T19:01:27.161Z","seq":35,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"fillText","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:27.163Z","seq":36,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"click","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:27.163Z","seq":37,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=login-submit-btn in page."}
+{"level":"info","time":"2026-06-26T19:01:27.195Z","seq":38,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"click","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:27.198Z","seq":39,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:27.198Z","seq":40,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=search-input in page."}
+{"level":"info","time":"2026-06-26T19:01:28.490Z","seq":41,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:28.498Z","seq":42,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:28.498Z","seq":43,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=search-input in page."}
+{"level":"info","time":"2026-06-26T19:01:28.500Z","seq":44,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:28.503Z","seq":45,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:28.505Z","seq":46,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","msg":"Removed page=239186fc-38e8-4837-8481-220447e809d4 from context=39202e9e-49fd-43c8-ace8-aefc1f8fc86a page stack"}
+{"level":"info","time":"2026-06-26T19:01:28.521Z","seq":47,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc","action":"closeAllBrowsers","status":"succeeded"}
+{"level":"error","time":"2026-06-26T19:01:28.523Z","seq":48,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Valid Password","test_id":"s1-t2","component":"browser-library","event_kind":"grpc_error","status":"failed","error_type":"Error","msg":"Error: Browser has been closed.\n at PlaywrightState.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:12594:13)\n at PlaywrightServer.getActiveBrowser (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13516:52)\n at PlaywrightServer.setTimeout (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/index.js:13734:56)\n at Object.onReceiveHalfClose (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server.js:1478:25)\n at BaseServerInterceptingCall.maybePushNextMessage (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:594:31)\n at BaseServerInterceptingCall.handleEndEvent (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:634:14)\n at ServerHttp2Stream. (/home/lukas/miniconda3/lib/python3.13/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:387:18)\n at ServerHttp2Stream.emit (node:events:531:35)\n at endReadableNT (node:internal/streams/readable:1698:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)"}
+{"level":"info","time":"2026-06-26T19:01:28.525Z","seq":49,"suite_id":"s1","suite_name":"Login","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:28.526Z","seq":50,"suite_id":"s1","suite_name":"Login","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:28.528Z","seq":51,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:28.528Z","seq":52,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"getBrowserCatalog","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:28.548Z","seq":53,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"newPage","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:28.548Z","seq":54,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"No active browser, creating a new one"}
+{"level":"info","time":"2026-06-26T19:01:28.590Z","seq":55,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Setting default timeout for context context=94ca32a1-e9f6-4c9c-9638-3ded8a5ebe3f to 10000"}
+{"level":"info","time":"2026-06-26T19:01:28.590Z","seq":56,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Changed active context: context=94ca32a1-e9f6-4c9c-9638-3ded8a5ebe3f"}
+{"level":"info","time":"2026-06-26T19:01:28.620Z","seq":57,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Video path: undefined"}
+{"level":"info","time":"2026-06-26T19:01:28.620Z","seq":58,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Changed active page"}
+{"level":"info","time":"2026-06-26T19:01:29.051Z","seq":59,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"newPage","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:29.054Z","seq":60,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"fillText","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:29.055Z","seq":61,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=password-input in page."}
+{"level":"info","time":"2026-06-26T19:01:29.075Z","seq":62,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"fillText","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:29.077Z","seq":63,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"click","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:29.077Z","seq":64,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=login-submit-btn in page."}
+{"level":"info","time":"2026-06-26T19:01:29.111Z","seq":65,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"click","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:29.114Z","seq":66,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:29.114Z","seq":67,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=login-error in page."}
+{"level":"info","time":"2026-06-26T19:01:29.139Z","seq":68,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"waitForElementState","status":"succeeded"}
+{"level":"info","time":"2026-06-26T19:01:29.142Z","seq":69,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","event_kind":"grpc","action":"getText","status":"started"}
+{"level":"info","time":"2026-06-26T19:01:29.142Z","seq":70,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Strict mode is enabled, find Locator with id=login-error in page."}
+{"level":"info","time":"2026-06-26T19:01:29.142Z","seq":71,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Getting text content with text type ROBOT_FRAMEWORK_BROWSER_NO_SET"}
+{"level":"info","time":"2026-06-26T19:01:29.142Z","seq":72,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Getting text content without text type"}
+{"level":"info","time":"2026-06-26T19:01:29.157Z","seq":73,"suite_id":"s1","suite_name":"Login","test_name":"Login.Login With Invalid Password","test_id":"s1-t3","component":"browser-library","msg":"Locator was not an ,