mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
pre-commit run pre-commit-hooks --all-files
This commit is contained in:
@@ -81,4 +81,4 @@ body:
|
||||
attributes:
|
||||
label: Relevant Log Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code.
|
||||
render: shell
|
||||
render: shell
|
||||
|
||||
@@ -8,4 +8,4 @@ contact_links:
|
||||
about: Please ask questions in our Discord community
|
||||
- name: 📖 Documentation
|
||||
url: https://docs.browser-use.com
|
||||
about: Check our documentation for answers first
|
||||
about: Check our documentation for answers first
|
||||
|
||||
@@ -46,10 +46,10 @@ body:
|
||||
description: If you have specific suggestions for how to improve the documentation, please share them
|
||||
placeholder: |
|
||||
The documentation could be improved by...
|
||||
|
||||
|
||||
Example:
|
||||
```python
|
||||
# Your suggested code example or text here
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
@@ -40,4 +40,4 @@ body:
|
||||
placeholder: |
|
||||
- Example use cases
|
||||
- Screenshots or mockups
|
||||
- Related issues or discussions
|
||||
- Related issues or discussions
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: pre-commit run check-toml --all-files
|
||||
- run: pre-commit run check-yaml --all-files
|
||||
- run: pre-commit run end-of-file-fixer --all-files
|
||||
- run: pre-commit run trailing-whitespace --all-files
|
||||
+1
-1
@@ -187,4 +187,4 @@ gcp-login.json
|
||||
*.json
|
||||
*.jsonl
|
||||
|
||||
uv.lock
|
||||
uv.lock
|
||||
|
||||
@@ -10,9 +10,9 @@ repos:
|
||||
]
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
|
||||
Vendored
+1
-1
@@ -85,4 +85,4 @@
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[](https://app.workweave.ai/reports/repository/org_T5Pvn3UBswTHIsN1dWS3voPg/881458615)
|
||||
|
||||
|
||||
🌐 Browser-use is the easiest way to connect your AI agents with the browser.
|
||||
🌐 Browser-use is the easiest way to connect your AI agents with the browser.
|
||||
|
||||
💡 See what others are building and share your projects in our [Discord](https://link.browser-use.com/discord) - we'd love to see what you create!
|
||||
|
||||
@@ -177,7 +177,7 @@ Email [Toby](mailto:tbiddle@loop11.com?subject=I%20want%20to%20join%20the%20UI/U
|
||||
If you use Browser Use in your research or project, please cite:
|
||||
|
||||
|
||||
|
||||
|
||||
```bibtex
|
||||
@software{browser_use2024,
|
||||
author = {Müller, Magnus and Žunič, Gregor},
|
||||
@@ -187,20 +187,16 @@ If you use Browser Use in your research or project, please cite:
|
||||
url = {https://github.com/browser-use/browser-use}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<div align="center"> <img src="https://github.com/user-attachments/assets/402b2129-b6ac-44d3-a217-01aea3277dce" width="400"/>
|
||||
|
||||
|
||||
<div align="center"> <img src="https://github.com/user-attachments/assets/402b2129-b6ac-44d3-a217-01aea3277dce" width="400"/>
|
||||
|
||||
[](https://x.com/gregpr07)
|
||||
[](https://x.com/mamagnus00)
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
Made with ❤️ in Zurich and San Francisco
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -17,4 +17,3 @@ Please include as much of the information listed below as you can to help me bet
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help me triage your report more quickly.
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ Common action sequences:
|
||||
|
||||
5. TASK COMPLETION:
|
||||
- Use the done action as the last action as soon as the ultimate task is complete
|
||||
- Dont use "done" before you are done with everything the user asked you, except you reach the last step of max_steps.
|
||||
- Dont use "done" before you are done with everything the user asked you, except you reach the last step of max_steps.
|
||||
- If you reach your last step, use the done action even if the task is not fully finished. Provide all the information you have gathered so far. If the ultimate task is completly finished set success to true. If not everything the user asked for is completed set success in done to false!
|
||||
- If you have to do something repeatedly for example the task says for "each", or "for all", or "x times", count always inside "memory" how many times you have done it and how many remain. Don't stop until you have completed like the task asked you. Only call done after the last step.
|
||||
- Don't hallucinate actions
|
||||
- Make sure you include everything you found out for the ultimate task in the done text parameter. Do not just say you are done, but include the requested information of the task.
|
||||
- Make sure you include everything you found out for the ultimate task in the done text parameter. Do not just say you are done, but include the requested information of the task.
|
||||
|
||||
6. VISUAL CONTEXT:
|
||||
- When an image is provided, use it to understand the page layout
|
||||
@@ -62,8 +62,8 @@ Common action sequences:
|
||||
- If you fill an input field and your action sequence is interrupted, most often something changed e.g. suggestions popped up under the field.
|
||||
|
||||
8. Long tasks:
|
||||
- Keep track of the status and subresults in the memory.
|
||||
- Keep track of the status and subresults in the memory.
|
||||
|
||||
9. Extraction:
|
||||
- If your task is to find information - call extract_content on the specific pages to get and store the information.
|
||||
Your responses must be always JSON with the specified format.
|
||||
Your responses must be always JSON with the specified format.
|
||||
|
||||
@@ -674,24 +674,24 @@ class BrowserContext:
|
||||
debug_script = """(() => {
|
||||
function getPageStructure(element = document, depth = 0, maxDepth = 10) {
|
||||
if (depth >= maxDepth) return '';
|
||||
|
||||
|
||||
const indent = ' '.repeat(depth);
|
||||
let structure = '';
|
||||
|
||||
|
||||
// Skip certain elements that clutter the output
|
||||
const skipTags = new Set(['script', 'style', 'link', 'meta', 'noscript']);
|
||||
|
||||
|
||||
// Add current element info if it's not the document
|
||||
if (element !== document) {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
|
||||
|
||||
// Skip uninteresting elements
|
||||
if (skipTags.has(tagName)) return '';
|
||||
|
||||
|
||||
const id = element.id ? `#${element.id}` : '';
|
||||
const classes = element.className && typeof element.className === 'string' ?
|
||||
const classes = element.className && typeof element.className === 'string' ?
|
||||
`.${element.className.split(' ').filter(c => c).join('.')}` : '';
|
||||
|
||||
|
||||
// Get additional useful attributes
|
||||
const attrs = [];
|
||||
if (element.getAttribute('role')) attrs.push(`role="${element.getAttribute('role')}"`);
|
||||
@@ -702,10 +702,10 @@ class BrowserContext:
|
||||
const src = element.getAttribute('src');
|
||||
attrs.push(`src="${src.substring(0, 50)}${src.length > 50 ? '...' : ''}"`);
|
||||
}
|
||||
|
||||
|
||||
// Add element info
|
||||
structure += `${indent}${tagName}${id}${classes}${attrs.length ? ' [' + attrs.join(', ') + ']' : ''}\\n`;
|
||||
|
||||
|
||||
// Handle iframes specially
|
||||
if (tagName === 'iframe') {
|
||||
try {
|
||||
@@ -721,7 +721,7 @@ class BrowserContext:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get all child elements
|
||||
const children = element.children || element.childNodes;
|
||||
for (const child of children) {
|
||||
@@ -729,10 +729,10 @@ class BrowserContext:
|
||||
structure += getPageStructure(child, depth + 1, maxDepth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return structure;
|
||||
}
|
||||
|
||||
|
||||
return getPageStructure();
|
||||
})()"""
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ documentation](https://playwright.dev/docs/api/class-browsercontext).
|
||||
|
||||
The agent is executed using the async `run()` method:
|
||||
|
||||
- `max_steps` (default: `100`)
|
||||
- `max_steps` (default: `100`)
|
||||
Maximum number of steps the agent can take during execution. This prevents infinite loops and helps control execution time.
|
||||
|
||||
## Agent History
|
||||
|
||||
@@ -125,4 +125,3 @@ controller = Controller(exclude_actions=['open_tab', 'search_google'])
|
||||
|
||||
|
||||
For more examples like file upload or notifications, visit [examples/custom-functions](https://github.com/browser-use/browser-use/tree/main/examples/custom-functions).
|
||||
|
||||
|
||||
@@ -47,4 +47,4 @@ async def main():
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
```
|
||||
```
|
||||
|
||||
@@ -43,8 +43,8 @@ if __name__ == '__main__':
|
||||
In this example:
|
||||
1. The model only sees `x_name` and `x_password` as placeholders.
|
||||
2. When the model wants to use your password it outputs x_password - and we replace it with the actual value.
|
||||
3. When your password is visable on the current page, we replace it in the LLM input - so that the model never has it in its state.
|
||||
3. When your password is visable on the current page, we replace it in the LLM input - so that the model never has it in its state.
|
||||
|
||||
Warning: Vision models still see the image of the page - where the sensitive data might be visible.
|
||||
|
||||
This approach ensures that sensitive information remains secure while still allowing the agent to perform tasks that require authentication.
|
||||
This approach ensures that sensitive information remains secure while still allowing the agent to perform tasks that require authentication.
|
||||
|
||||
@@ -138,7 +138,7 @@ GEMINI_API_KEY=
|
||||
|
||||
|
||||
### DeepSeek-V3
|
||||
The community likes DeepSeek-V3 for its low price, no rate limits, open-source nature, and good performance.
|
||||
The community likes DeepSeek-V3 for its low price, no rate limits, open-source nature, and good performance.
|
||||
The example is available [here](https://github.com/browser-use/browser-use/blob/main/examples/models/deepseek.py).
|
||||
|
||||
```python
|
||||
@@ -192,7 +192,7 @@ DEEPSEEK_API_KEY=
|
||||
```
|
||||
|
||||
### Ollama
|
||||
Many users asked for local models. Here they are.
|
||||
Many users asked for local models. Here they are.
|
||||
|
||||
1. Download Ollama from [here](https://ollama.ai/download)
|
||||
2. Run `ollama pull model_name`. Pick a model which supports tool-calling from [here](https://ollama.com/search?c=tools)
|
||||
|
||||
@@ -98,7 +98,7 @@ We suggest using extensions on your IDE to recognize and format MDX. If you're a
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Issue: Encountering an unknown error">
|
||||
|
||||
|
||||
Solution: Go to the root of your device and delete the \~/.mintlify folder. Afterwards, run `mintlify dev` again.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -63,4 +63,4 @@ In the trace view, you can also see the agent's current step, the tool it's usin
|
||||
|
||||
## Laminar
|
||||
|
||||
To learn more about tracing and evaluating your browser agents, check out the [Laminar docs](https://docs.lmnr.ai).
|
||||
To learn more about tracing and evaluating your browser agents, check out the [Laminar docs](https://docs.lmnr.ai).
|
||||
|
||||
@@ -73,4 +73,4 @@ To run this example, you need to install the following packages:
|
||||
You can install these packages using pip:
|
||||
|
||||
```sh
|
||||
pip install fastapi uvicorn slack_sdk
|
||||
pip install fastapi uvicorn slack_sdk
|
||||
|
||||
@@ -20,7 +20,7 @@ class SlackBot:
|
||||
def __init__(self, llm: BaseChatModel, bot_token: str, signing_secret: str, ack: bool = False, browser_config: BrowserConfig = BrowserConfig(headless=True)):
|
||||
if not bot_token or not signing_secret:
|
||||
raise ValueError("Bot token and signing secret must be provided")
|
||||
|
||||
|
||||
self.llm = llm
|
||||
self.ack = ack
|
||||
self.browser_config = browser_config
|
||||
@@ -108,4 +108,4 @@ async def slack_events(request: Request, slack_bot: SlackBot = Depends()):
|
||||
return {}
|
||||
except Exception as e:
|
||||
logger.error(f"Error in slack_events: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Internal Server Error")
|
||||
raise HTTPException(status_code=500, detail="Internal Server Error")
|
||||
|
||||
@@ -21,7 +21,7 @@ azure_openai_endpoint = os.environ.get('AZURE_OPENAI_ENDPOINT')
|
||||
|
||||
# Initialize the Azure OpenAI client
|
||||
llm = AzureChatOpenAI(
|
||||
model_name='gpt-4o',
|
||||
model_name='gpt-4o',
|
||||
openai_api_key=azure_openai_api_key,
|
||||
azure_endpoint=azure_openai_endpoint, # Corrected to use azure_endpoint instead of openai_api_base
|
||||
deployment_name='gpt-4o', # Use deployment_name for Azure models
|
||||
@@ -39,4 +39,4 @@ async def main():
|
||||
input('Press Enter to continue...')
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -757,4 +757,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|------------------------|-------------------|-------------------------------------------|-------------------------------------------|
|
||||
| `command_line.py` | **Terminal** | Parses arguments for command-line execution. | `python command_line.py` |
|
||||
| `gradio_demo.py` | **Gradio** | Provides a Gradio-based interactive UI. | `python gradio_demo.py` |
|
||||
| `streamlit_demo.py` | **Streamlit** | Runs a Streamlit-based web interface. | `python -m streamlit run streamlit_demo.py` |
|
||||
| `streamlit_demo.py` | **Streamlit** | Runs a Streamlit-based web interface. | `python -m streamlit run streamlit_demo.py` |
|
||||
|
||||
@@ -34,7 +34,7 @@ def get_llm(provider: str):
|
||||
api_key = os.getenv("ANTHROPIC_API_KEY")
|
||||
if not api_key:
|
||||
raise ValueError("Error: ANTHROPIC_API_KEY is not set. Please provide a valid API key.")
|
||||
|
||||
|
||||
return ChatAnthropic(
|
||||
model_name='claude-3-5-sonnet-20240620', timeout=25, stop=None, temperature=0.0
|
||||
)
|
||||
@@ -43,7 +43,7 @@ def get_llm(provider: str):
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
if not api_key:
|
||||
raise ValueError("Error: OPENAI_API_KEY is not set. Please provide a valid API key.")
|
||||
|
||||
|
||||
return ChatOpenAI(model='gpt-4o', temperature=0.0)
|
||||
|
||||
else:
|
||||
@@ -88,10 +88,10 @@ async def main():
|
||||
agent, browser = initialize_agent(args.query, args.provider)
|
||||
|
||||
await agent.run(max_steps=25)
|
||||
|
||||
|
||||
input('Press Enter to close the browser...')
|
||||
await browser.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -105,4 +105,4 @@ def create_ui():
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo = create_ui()
|
||||
demo.launch()
|
||||
demo.launch()
|
||||
|
||||
@@ -29,7 +29,7 @@ def get_llm(provider: str):
|
||||
if not api_key:
|
||||
st.error("Error: ANTHROPIC_API_KEY is not set. Please provide a valid API key.")
|
||||
st.stop()
|
||||
|
||||
|
||||
return ChatAnthropic(
|
||||
model_name='claude-3-5-sonnet-20240620', timeout=25, stop=None, temperature=0.0
|
||||
)
|
||||
@@ -39,7 +39,7 @@ def get_llm(provider: str):
|
||||
if not api_key:
|
||||
st.error("Error: OPENAI_API_KEY is not set. Please provide a valid API key.")
|
||||
st.stop()
|
||||
|
||||
|
||||
return ChatOpenAI(model='gpt-4o', temperature=0.0)
|
||||
else:
|
||||
st.error(f'Unsupported provider: {provider}')
|
||||
|
||||
@@ -10,6 +10,3 @@
|
||||
| `scrolling_page.py` | Automates webpage scrolling with various scrolling actions and text search functionality. |
|
||||
| `twitter_post_using_cookies.py` | Automates posting on X (Twitter) using stored authentication cookies. |
|
||||
| `web_voyager_agent.py` | A general-purpose web navigation agent for tasks like flight booking and course searching. |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,4 +33,4 @@ async def main():
|
||||
input('Press Enter to exit')
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Goal: Searches for job listings, evaluates relevance based on a CV, and applies
|
||||
Goal: Searches for job listings, evaluates relevance based on a CV, and applies
|
||||
|
||||
@dev You need to add OPENAI_API_KEY to your environment variables.
|
||||
Also you have to install PyPDF2 to read pdf files: pip install PyPDF2
|
||||
@@ -157,4 +157,4 @@ async def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -43,4 +43,4 @@ async def main():
|
||||
await coder.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -124,4 +124,4 @@ async def main():
|
||||
await agent.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -8,7 +8,7 @@ import asyncio
|
||||
task="""
|
||||
### Prompt for Shopping Agent – Migros Online Grocery Order
|
||||
|
||||
**Objective:**
|
||||
**Objective:**
|
||||
Visit [Migros Online](https://www.migros.ch/en), search for the required grocery items, add them to the cart, select an appropriate delivery window, and complete the checkout process using TWINT.
|
||||
|
||||
**Important:**
|
||||
@@ -89,8 +89,8 @@ At this stage, check the basket on the top right (indicates the price) and check
|
||||
- Proceed to checkout.
|
||||
- Select **TWINT** as the payment method.
|
||||
- Check out.
|
||||
-
|
||||
- if it's needed the userename is: nikoskalio.dev@gmail.com
|
||||
-
|
||||
- if it's needed the userename is: nikoskalio.dev@gmail.com
|
||||
- and the password is : TheCircuit.Migros.dev!
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Goal: Automates posting on X (Twitter) using stored authentication cookies.
|
||||
# Goal: Automates posting on X (Twitter) using stored authentication cookies.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
@@ -35,7 +35,7 @@ async def test_standard_browser_launch(monkeypatch):
|
||||
@pytest.mark.asyncio
|
||||
async def test_cdp_browser_launch(monkeypatch):
|
||||
"""
|
||||
Test that when a CDP URL is provided in the configuration, the Browser uses _setup_cdp
|
||||
Test that when a CDP URL is provided in the configuration, the Browser uses _setup_cdp
|
||||
and returns the expected DummyBrowser.
|
||||
"""
|
||||
class DummyBrowser:
|
||||
@@ -87,7 +87,7 @@ async def test_wss_browser_launch(monkeypatch):
|
||||
@pytest.mark.asyncio
|
||||
async def test_chrome_instance_browser_launch(monkeypatch):
|
||||
"""
|
||||
Test that when a chrome instance path is provided the Browser class uses
|
||||
Test that when a chrome instance path is provided the Browser class uses
|
||||
_setup_browser_with_instance branch and returns the expected DummyBrowser object
|
||||
by reusing an existing Chrome instance.
|
||||
"""
|
||||
@@ -303,4 +303,4 @@ async def test_standard_browser_launch_with_proxy(monkeypatch):
|
||||
# Call get_playwright_browser and verify that the returned browser is as expected.
|
||||
result_browser = await browser_obj.get_playwright_browser()
|
||||
assert isinstance(result_browser, DummyBrowser), "Expected DummyBrowser from _setup_standard_browser with proxy provided"
|
||||
await browser_obj.close()
|
||||
await browser_obj.close()
|
||||
|
||||
@@ -9,7 +9,7 @@ from unittest.mock import Mock
|
||||
|
||||
def test_is_url_allowed():
|
||||
"""
|
||||
Test the _is_url_allowed method to verify that it correctly checks URLs against
|
||||
Test the _is_url_allowed method to verify that it correctly checks URLs against
|
||||
the allowed domains configuration.
|
||||
Scenario 1: When allowed_domains is None, all URLs should be allowed.
|
||||
Scenario 2: When allowed_domains is a list, only URLs matching the allowed domain(s) are allowed.
|
||||
@@ -151,7 +151,7 @@ async def test_get_scroll_info():
|
||||
"""
|
||||
Test the get_scroll_info method by mocking the page's evaluate method.
|
||||
This dummy page returns preset values for window.scrollY, window.innerHeight,
|
||||
and document.documentElement.scrollHeight. The test then verifies that the
|
||||
and document.documentElement.scrollHeight. The test then verifies that the
|
||||
computed scroll information (pixels_above and pixels_below) match the expected values.
|
||||
"""
|
||||
# Define a dummy page with an async evaluate method returning preset values.
|
||||
@@ -323,4 +323,4 @@ async def test_remove_highlights_failure():
|
||||
try:
|
||||
await context.remove_highlights()
|
||||
except Exception as e:
|
||||
pytest.fail(f"remove_highlights raised an exception: {e}")
|
||||
pytest.fail(f"remove_highlights raised an exception: {e}")
|
||||
|
||||
@@ -19,18 +19,18 @@ async def test_dropdown(llm, browser_context):
|
||||
try:
|
||||
history: AgentHistoryList = await agent.run(20)
|
||||
result = history.final_result()
|
||||
|
||||
|
||||
# Verify dropdown interaction
|
||||
assert result is not None
|
||||
assert 'Duck' in result, "Expected 5th option 'Duck' to be selected"
|
||||
|
||||
|
||||
# Verify dropdown state
|
||||
element = await browser_context.get_element_by_selector('select')
|
||||
assert element is not None, "Dropdown element should exist"
|
||||
|
||||
|
||||
value = await element.evaluate('el => el.value')
|
||||
assert value == '5', "Dropdown should have 5th option selected"
|
||||
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"Dropdown test failed: {str(e)}")
|
||||
finally:
|
||||
|
||||
@@ -19,22 +19,22 @@ async def test_dropdown_complex(llm, browser_context):
|
||||
try:
|
||||
history: AgentHistoryList = await agent.run(20)
|
||||
result = history.final_result()
|
||||
|
||||
|
||||
# Verify dropdown interaction
|
||||
assert result is not None
|
||||
assert 'json' in result.lower(), "Expected 'json' option to be selected"
|
||||
|
||||
|
||||
# Verify dropdown state
|
||||
element = await browser_context.get_element_by_selector('.select-selected')
|
||||
assert element is not None, "Custom dropdown element should exist"
|
||||
|
||||
|
||||
text = await element.text_content()
|
||||
assert 'json' in text.lower(), "Dropdown should display json option"
|
||||
|
||||
|
||||
# Verify the selected option's effect
|
||||
code_element = await browser_context.get_element_by_selector('pre code')
|
||||
assert code_element is not None, "Code element should be visible when JSON is selected"
|
||||
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"Complex dropdown test failed: {str(e)}")
|
||||
finally:
|
||||
|
||||
@@ -40,4 +40,3 @@ async def test_gif_path():
|
||||
assert result is not None
|
||||
|
||||
assert os.path.exists("./google.gif"), "google.gif was not created"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user