{"id":20414310,"url":"https://github.com/tnypxl/basinframework","last_synced_at":"2026-02-25T06:13:28.883Z","repository":{"id":40886615,"uuid":"246095251","full_name":"tnypxl/BasinFramework","owner":"tnypxl","description":"An opinionated browser test framework built around Selenium WebDriver","archived":false,"fork":false,"pushed_at":"2022-12-08T10:00:10.000Z","size":359,"stargazers_count":3,"open_issues_count":10,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T17:17:19.520Z","etag":null,"topics":["basin","csharp","dotnet","hacktoberfest","selenium","selenium-webdriver","webdriver"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tnypxl.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-09T17:08:26.000Z","updated_at":"2021-11-22T16:02:39.000Z","dependencies_parsed_at":"2023-01-25T06:30:18.650Z","dependency_job_id":null,"html_url":"https://github.com/tnypxl/BasinFramework","commit_stats":null,"previous_names":["tnypxl/basinframeworkdotnetcore"],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tnypxl%2FBasinFramework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tnypxl%2FBasinFramework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tnypxl%2FBasinFramework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tnypxl%2FBasinFramework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tnypxl","download_url":"https://codeload.github.com/tnypxl/BasinFramework/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248600099,"owners_count":21131424,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["basin","csharp","dotnet","hacktoberfest","selenium","selenium-webdriver","webdriver"],"created_at":"2024-11-15T06:08:57.001Z","updated_at":"2026-02-25T06:13:28.829Z","avatar_url":"https://github.com/tnypxl.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Basin Framework\n\n![Basin Build](https://github.com/tnypxl/BasinFrameworkDotNetCore/workflows/Basin%20Build/badge.svg)\n\nAn opinionated browser test framework for Selenium WebDriver. There are many like this one. The goal here was to build something that was easy to implement while still providing flexibility without over abstracting.\n\n## Getting stared\n\n### Install\n\n```shell\ndotnet add package BasinFramework\n```\n\n### Configuration\n\nCreate a JSON file at the root of your project\n\n```json\n{\n    \"Environment\": {\n        \"Site\": \"integration\",\n        \"Browser\": \"Local Chrome\",\n        \"Login\": \"lebronjaymes\"\n    },\n\n    \"Sites\": [\n        {\n            \"Id\": \"staging\",\n            \"Url\": \"https://staging.coolapp.com\"\n        },\n        {\n            \"Id\": \"integration\",\n            \"Url\": \"https://integration.coolapp.com\"\n        },\n        {\n            \"Id\": \"preprod\",\n            \"Url\": \"http://preprod.coolapp.com\"\n        }\n    ],\n\n    \"Logins\": [\n        {\n            \"Role\": \"Customer\",\n            \"Username\": \"JordanTheGOAT\",\n            \"Password\":  \"givemetheball\" \n        },\n        {\n            \"Role\": \"Admin\",\n            \"Username\": \"lebronjaymes\",\n            \"Password\": \"foulsmakemecry\"\n        }\n    ],\n\n    \"Browsers\": [\n        {\n            \"Id\": \"Local Chrome\",\n            \"Kind\": \"chrome\",\n            \"Timeout\": 10\n        }\n    ]\n}\n```\n\nInitialize the config file:\n\n```csharp\nBasinEnv.SetConfig(\"path/to/json/config/file.json\");\n```\n\nThen you access config data with `BasinEnv`\n\n```csharp\nBasinEnv.Site.Id; // returns \"integration\"\nBasinEnv.Site.Url; // returns \"https://integration.coolapp.com\"\nBasinEnv.Browser.Kind; // returns \"chrome\"\nBasinEnv.Login.Username; // returns \"lebronjaymes\"\n```\n\nOr switch to an different configuration\n\n```c#\nBasinEnv.UseSite(\"Some Other Site Id\");\nBasinEnv.UseBrowser(\"Some Other Browser Id\");\n```\n\nOr use a config object\n\n```csharp\nBasinEnv.UseSite(new SiteConfig() {\n    Id = \"My New Site\",\n    Url = \"http://dopesites.example.com\"\n});\n```\n\nNote: If you use these methods before calling `BasinEnv.SetConfig()`, they will be overwritten with settings defined in the JSON config file.\n\n### Start a browser session\n\nBy default, all the drivers are configured with a clean slate. That just means there are no browser flags or custom binary paths being set initially. Starting up a browser and going to a url is 2 lines of code.\n\n```c#\n// Uses the value defined in `Environment.Browser` by default to load a listed browser config by its `Id`\nBrowserSession.Init(); \n\n// Navigates to a given url\nBrowserSession.Goto(\"http://someurl\");\n```\n\nFor the most part, nothing else is needed. But if you need to access the `IWebDriver` instance, just call `BrowserSession.Current`.\n\n### Use a `IWebDriver` instance directly\n\nMaybe I already have code written to manage driver instances. Just pass in an instance of IWebDriver.\n\n```c#\nBrowserSession.Init(new ChromeDriver());\nBrowserSession.Goto(\"http://someurl\");\n```\n\n### Creating simple page object class\n\nBasin provides classes and interfaces to ease the pain of building page object frameworks. Below is an example login page.\n\n```c#\nusing Basin.Selenium;\nusing Basin.Pages;\nusing OpenQA.Selenium;\n\nnamespace Example\n{\n    public class LoginPage : Page\n    { \n        // Page elements\n        public Element UsernameField =\u003e TextInputTag.WithId(\"username\");\n        public Element PasswordField =\u003e InputTag.WithAttr(\"type\", \"password\").WithId(\"password\");\n        public Element Submit =\u003e ButtonTag.WithAttr(\"name\", \"submitLogin\");\n\n        // Page behavior\n        public void Login(string username, string password)\n        {\n            UsernameField.SendKeys(username);\n            PasswordField.SendKeys(password);\n            Submit.Click();\n        }\n    }\n}\n```\n\n### Creating a page object map\n\nPage object classes can grow very large over time. I've found it incredibly difficult to retain readability and clarity with a single class. So instead of keeping everything in a single massive class, I break it down into to 2 classes. A page class for behaviors and a page map class for storing the element locators. Below its how its accomplished:\n\n```c#\nusing Basin.Selenium;\nusing Basin.Pages;\nusing OpenQA.Selenium;\n\nnamespace Example\n{\n    // This class no longer includes locator methods.\n    // The goal is to only put behavior methods in this class.\n    public class LoginPage : Page\u003cLoginPageMap\u003e\n    {\n        public void LoginWith(string username, string password)\n        {\n            Map.UsernameField.SendKeys(username);\n            Map.PasswordField.SendKeys(password);\n            Map.Submit.Click();\n        }\n    }\n\n    // PageMap provides the locator methods.\n    // The goal is to only put Element definitions in this class.\n    public class LoginPageMap : PageMap\n    {\n        public Element UsernameField =\u003e TextInputTag.WithId(\"username\");\n        public Element PasswordField =\u003e InputTag.WithAttr(\"type\", \"password\").WithId(\"password\");\n        public Element Submit =\u003e ButtonTag.WithAttr(\"name\", \"submitLogin\");\n    }\n}\n```\n\nNow I have clean separate APIs for calling page elements and behaviors. Page map classes are portable and can be used in other classes that need the same element locators.\n\nHow page objects should be organized is quite subjective, but the goal of these interfaces and abstracts classes is to provide some basic structure without getting in the way.\n\n## Contribute\n\n1. Fork the repo\n2. Create a branch\n3. Create a PR based on your fork branch\n\n## Credit\n\nThanks to [ElSnoMan](https://github.com/ElSnoMan) and\n[Test Automation University](https://testautomationu.applitools.com/)\nfrom Applitools for this [Course](https://testautomationu.applitools.com/test-automation-framework-csharp/)\n\nBasin is based on and heavily inspired by the source code from [this repo](https://github.com/ElSnoMan/from-scripting-to-framework)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftnypxl%2Fbasinframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftnypxl%2Fbasinframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftnypxl%2Fbasinframework/lists"}