├── .gitignore ├── Esports ├── Esports.sln ├── Framework │ ├── Constants │ │ └── Leagues.cs │ ├── Data │ │ ├── example.json │ │ └── tournamentPlayerStats.json │ ├── FW.cs │ ├── Framework.csproj │ ├── Model │ │ ├── GameIdMapping.cs │ │ ├── Player.cs │ │ ├── PlayerStats.cs │ │ ├── ScheduleItem.cs │ │ ├── Team.cs │ │ ├── TeamRosterStats.cs │ │ ├── TeamStanding.cs │ │ ├── TeamStatsHistory.cs │ │ └── TeamStatsSummary.cs │ ├── Selenium │ │ ├── Driver.cs │ │ ├── DriverFactory.cs │ │ ├── Element.cs │ │ ├── Elements.cs │ │ └── WindowManager.cs │ └── Services │ │ ├── IPlayerStatsService.cs │ │ ├── LocalPlayerService.cs │ │ └── PlayerService.cs ├── League.Com │ ├── Esports.cs │ ├── League.Com.csproj │ └── Pages │ │ ├── Base │ │ ├── EsportsMenu.cs │ │ ├── PageBase.cs │ │ └── WatchMenu.cs │ │ ├── HomePage.cs │ │ ├── ServiceStatusPage.cs │ │ ├── StandingsPage.cs │ │ ├── SupportPage.cs │ │ └── TicketsPage.cs ├── Tests │ ├── Base │ │ └── TestBase.cs │ ├── League.Com.Tests.csproj │ ├── Services.cs │ ├── Standings.cs │ ├── Support.cs │ └── Tickets.cs ├── _drivers │ ├── mac │ │ └── chromedriver │ └── windows │ │ └── chromedriver.exe ├── config.json ├── docker-compose.yml └── failed_to_click_.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.deps.json 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Object files 8 | *.o 9 | *.ko 10 | *.obj 11 | *.elf 12 | 13 | # Linker output 14 | *.ilk 15 | *.map 16 | *.exp 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | 56 | *.cache 57 | 58 | *.tmp 59 | 60 | *.userprefs 61 | 62 | *.swp 63 | *.*~ 64 | project.lock.json 65 | .DS_Store 66 | *.pyc 67 | nupkg/ 68 | 69 | # Visual Studio Code 70 | .vscode 71 | 72 | # User-specific files 73 | *.suo 74 | *.user 75 | *.userosscache 76 | *.sln.docstates 77 | 78 | # Build results 79 | [Dd]ebug/ 80 | [Dd]ebugPublic/ 81 | [Rr]elease/ 82 | [Rr]eleases/ 83 | x64/ 84 | x86/ 85 | build/ 86 | bld/ 87 | bin/ 88 | [Oo]bj/ 89 | [Oo]ut/ 90 | msbuild.log 91 | msbuild.err 92 | msbuild.wrn 93 | 94 | # Visual Studio 95 | .vs/ 96 | *.ide 97 | 98 | *.ide-shm 99 | 100 | *.ide-wal 101 | 102 | *.log 103 | .idea 104 | 105 | TestResult\.xml 106 | -------------------------------------------------------------------------------- /Esports/Esports.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "League.Com.Tests", "Tests\League.Com.Tests.csproj", "{04F935FB-BE7A-4AE1-81FE-72950C7B97E6}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{CCFF45A2-3672-451D-80DB-DF221D4A4383}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "League.Com", "League.Com\League.Com.csproj", "{20457779-3A50-4A57-B505-3708BEBA7830}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {04F935FB-BE7A-4AE1-81FE-72950C7B97E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {04F935FB-BE7A-4AE1-81FE-72950C7B97E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {04F935FB-BE7A-4AE1-81FE-72950C7B97E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {04F935FB-BE7A-4AE1-81FE-72950C7B97E6}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {CCFF45A2-3672-451D-80DB-DF221D4A4383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {CCFF45A2-3672-451D-80DB-DF221D4A4383}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {CCFF45A2-3672-451D-80DB-DF221D4A4383}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {CCFF45A2-3672-451D-80DB-DF221D4A4383}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {20457779-3A50-4A57-B505-3708BEBA7830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {20457779-3A50-4A57-B505-3708BEBA7830}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {20457779-3A50-4A57-B505-3708BEBA7830}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {20457779-3A50-4A57-B505-3708BEBA7830}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /Esports/Framework/Constants/Leagues.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.Constants 2 | { 3 | public class Leagues 4 | { 5 | public const string LCS = "LCS"; 6 | public const string LEC = "LEC"; 7 | public const string LCK = "LCK"; 8 | public const string LPL = "LPL"; 9 | public const string LCS_ACADEMY = "LCS Academy"; 10 | public const string TCL = "TCL"; 11 | public const string CBLOL = "CBLOL"; 12 | public const string LLA = "LLA"; 13 | public const string OPL = "OPL"; 14 | public const string WORLDS = "World Championship"; 15 | public const string ALLSTARS = "All-Star Event"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Esports/Framework/Data/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "height": 11, 4 | "width": 8 5 | }, 6 | 7 | "array": [ 8 | "apples", 9 | "oranges", 10 | "bananas" 11 | ], 12 | 13 | "array_of_objects": [ 14 | { 15 | "id": 1, 16 | "name": "Han Solo" 17 | }, 18 | { 19 | "id": 2, 20 | "name": "Charlie Brown" 21 | } 22 | ], 23 | 24 | "complex": [ 25 | { 26 | "name": "Anthony Hopkins", 27 | "email": "anthony@hopkins.com", 28 | "age": 73, 29 | "roles": [ 30 | "actor", 31 | "writer", 32 | "director" 33 | ], 34 | "address": { 35 | "addr1": "123 Fun Ave", 36 | "addr2": "Apt 2", 37 | "city": "Smallville", 38 | "state": "Arkansas", 39 | "postal": 12345 40 | } 41 | }, 42 | { 43 | "name": "Donald Trump", 44 | "email": "donnie@trump.com", 45 | "age": 60, 46 | "roles": [ 47 | "reality star", 48 | "tweet master", 49 | "president" 50 | ], 51 | "address": { 52 | "addr1": "White House", 53 | "addr2": null, 54 | "city": "wrong", 55 | "state": "you're fake news", 56 | "postal": 12345 57 | } 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /Esports/Framework/Data/tournamentPlayerStats.json: -------------------------------------------------------------------------------- 1 | { 2 | "stats": [ 3 | { 4 | "id": 45, 5 | "name": "Impact", 6 | "position": "toplane", 7 | "playerSlug": "impact", 8 | "team": "TL", 9 | "gamesPlayed": 10, 10 | "kda": 6.625, 11 | "kills": 19, 12 | "deaths": 16, 13 | "assists": 87, 14 | "killParticipation": 0.7681159420289855, 15 | "csPerMin": 7.729004645336396, 16 | "cs": 2468, 17 | "minutesPlayed": 319, 18 | "teamSlug": "team-liquid" 19 | }, 20 | { 21 | "id": 60, 22 | "name": "Bjergsen", 23 | "position": "midlane", 24 | "playerSlug": "bjergsen", 25 | "team": "TSM", 26 | "gamesPlayed": 4, 27 | "kda": 6.666666666666667, 28 | "kills": 8, 29 | "deaths": 3, 30 | "assists": 12, 31 | "killParticipation": 0.8695652173913043, 32 | "csPerMin": 10.308003029537996, 33 | "cs": 1361, 34 | "minutesPlayed": 132, 35 | "teamSlug": "team-solomid" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /Esports/Framework/FW.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Newtonsoft.Json; 4 | 5 | namespace Framework 6 | { 7 | public static class FW 8 | { 9 | public static void Init() 10 | { 11 | if (_configuration == null) 12 | { 13 | var jsonString = File.ReadAllText(WORKSPACE_DIRECTORY + "/config.json"); 14 | _configuration = JsonConvert.DeserializeObject(jsonString); 15 | } 16 | } 17 | 18 | public static Config Config => _configuration ?? throw new Exception("_configuration null. Call Init() first."); 19 | 20 | public static string WORKSPACE_DIRECTORY => Path.GetFullPath(@"../../../../"); 21 | 22 | private static Config _configuration; 23 | } 24 | 25 | public class Config 26 | { 27 | public DriverSettings Driver { get; set; } 28 | } 29 | 30 | public class DriverSettings 31 | { 32 | public string Browser { get; set; } 33 | 34 | public string Type { get; set; } 35 | 36 | public int Wait { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Esports/Framework/Framework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Esports/Framework/Model/GameIdMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Framework.Model 5 | { 6 | public class GameIdMapping 7 | { 8 | [JsonProperty("gameHash")] 9 | public string GameHash { get; set; } 10 | 11 | [JsonProperty("id")] 12 | public Guid Id { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Esports/Framework/Model/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | 5 | namespace Framework.Model 6 | { 7 | public class Player 8 | { 9 | [JsonProperty("bios")] 10 | public dynamic Bios { get; set; } 11 | 12 | [JsonProperty("birthdate")] 13 | public DateTime BirthDate { get; set; } 14 | 15 | [JsonProperty("champions")] 16 | public List Champions { get; set; } 17 | 18 | [JsonProperty("createdAt")] 19 | public DateTime CreatedAt { get; set; } 20 | 21 | [JsonProperty("firstName")] 22 | public string FirstName { get; set; } 23 | 24 | [JsonProperty("foreignIds")] 25 | public dynamic ForeignIds { get; set; } 26 | 27 | [JsonProperty("hometown")] 28 | public string HomeTown { get; set; } 29 | 30 | [JsonProperty("id")] 31 | public int Id { get; set; } 32 | 33 | [JsonProperty("lastName")] 34 | public string LastName { get; set; } 35 | 36 | [JsonProperty("name")] 37 | public string Name { get; set; } 38 | 39 | [JsonProperty("photoUrl")] 40 | public string PhotoUrl { get; set; } 41 | 42 | [JsonProperty("region")] 43 | public string Region { get; set; } 44 | 45 | [JsonProperty("roleSlug")] 46 | public string RoleSlug { get; set; } 47 | 48 | [JsonProperty("slug")] 49 | public string Slug { get; set; } 50 | 51 | [JsonProperty("socialNetworks")] 52 | public dynamic SocialNetworks { get; set; } 53 | 54 | [JsonProperty("teamRosterStat")] 55 | public string TeamRosterStat { get; set; } 56 | 57 | [JsonProperty("updatedAt")] 58 | public DateTime UpdatedAt { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Esports/Framework/Model/PlayerStats.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Framework.Model 4 | { 5 | public class PlayerStats 6 | { 7 | [JsonProperty("id")] 8 | public int Id { get; set; } 9 | 10 | [JsonProperty("name")] 11 | public string Name { get; set; } 12 | 13 | [JsonProperty("position")] 14 | public string Position { get; set; } 15 | 16 | [JsonProperty("playerSlug")] 17 | public string PlayerSlug { get; set; } 18 | 19 | [JsonProperty("team")] 20 | public string Team { get; set; } 21 | 22 | [JsonProperty("gamesPlayed")] 23 | public int GamesPlayed { get; set; } 24 | 25 | [JsonProperty("kda")] 26 | public double KDA { get; set; } 27 | 28 | [JsonProperty("kills")] 29 | public int Kills { get; set; } 30 | 31 | [JsonProperty("deaths")] 32 | public int Deaths { get; set; } 33 | 34 | [JsonProperty("assists")] 35 | public int Assists { get; set; } 36 | 37 | [JsonProperty("killParticipation")] 38 | public double KillParticipation { get; set; } 39 | 40 | [JsonProperty("csPerMin")] 41 | public double CsPerMin { get; set; } 42 | 43 | [JsonProperty("cs")] 44 | public int Cs { get; set; } 45 | 46 | [JsonProperty("minutesPlayed")] 47 | public int MinutesPlayed { get; set; } 48 | 49 | [JsonProperty("teamSlug")] 50 | public string TeamSlug { get; set; } 51 | } 52 | } -------------------------------------------------------------------------------- /Esports/Framework/Model/ScheduleItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Framework.Model 5 | { 6 | public class ScheduleItem 7 | { 8 | [JsonProperty("bracket")] 9 | public Guid Bracket { get; set; } 10 | 11 | [JsonProperty("content")] 12 | public string Content { get; set; } 13 | 14 | [JsonProperty("id")] 15 | public string Id { get; set; } 16 | 17 | [JsonProperty("league")] 18 | public int League { get; set; } 19 | 20 | [JsonProperty("match")] 21 | public Guid Match { get; set; } 22 | 23 | [JsonProperty("scheduledTime")] 24 | public DateTime ScheduledTime { get; set; } 25 | 26 | [JsonProperty("tags")] 27 | public dynamic Tags { get; set; } 28 | 29 | [JsonProperty("tournament")] 30 | public Guid Tournament { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Esports/Framework/Model/Team.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Framework.Model 5 | { 6 | public class Team 7 | { 8 | [JsonProperty("acronym")] 9 | public string Acronym { get; set; } 10 | 11 | [JsonProperty("altLogoUrl")] 12 | public string AltLogoUrl { get; set; } 13 | 14 | [JsonProperty("bios")] 15 | public dynamic Bios { get; set; } 16 | 17 | [JsonProperty("createdAt")] 18 | public DateTime CreatedAt { get; set; } 19 | 20 | [JsonProperty("foreignIds")] 21 | public dynamic ForeignIds { get; set; } 22 | 23 | [JsonProperty("guid")] 24 | public Guid Guid { get; set; } 25 | 26 | [JsonProperty("homeLeague")] 27 | public string HomeLeague { get; set; } 28 | 29 | [JsonProperty("id")] 30 | public int Id { get; set; } 31 | 32 | [JsonProperty("logoUrl")] 33 | public string LogoUrl { get; set; } 34 | 35 | [JsonProperty("name")] 36 | public string Name { get; set; } 37 | 38 | [JsonProperty("players")] 39 | public int[] PlayerIds { get; set; } 40 | 41 | [JsonProperty("slug")] 42 | public string Slug { get; set; } 43 | 44 | [JsonProperty("starters")] 45 | public int[] Starters { get; set; } 46 | 47 | [JsonProperty("subs")] 48 | public int[] Subs { get; set; } 49 | 50 | [JsonProperty("teamPhotoUrl")] 51 | public string TeamPhotoUrl { get; set; } 52 | 53 | [JsonProperty("updatedAt")] 54 | public DateTime UpdatedAt { get; set; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Esports/Framework/Model/TeamRosterStats.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Framework.Model 4 | { 5 | public class TeamRosterStats 6 | { 7 | [JsonProperty("averageAssists")] 8 | public double AvgAssists { get; set; } 9 | 10 | [JsonProperty("averageDeaths")] 11 | public double AvgDeaths { get; set; } 12 | 13 | [JsonProperty("averageKillParticipation")] 14 | public double AvgKillParticipation { get; set; } 15 | 16 | [JsonProperty("averageKills")] 17 | public double AvgKills { get; set; } 18 | 19 | [JsonProperty("championIds")] 20 | public int[] ChampionIds { get; set; } 21 | 22 | [JsonProperty("gamesPlayed")] 23 | public int GamesPlayed { get; set; } 24 | 25 | [JsonProperty("playerId")] 26 | public string PlayerId { get; set; } 27 | 28 | [JsonProperty("summonerName")] 29 | public string SummonerName { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Esports/Framework/Model/TeamStanding.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Framework.Model 3 | { 4 | public class TeamStanding 5 | { 6 | public int Rank { get; set; } 7 | public string Name { get; set; } 8 | public string Record { get; set; } 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Esports/Framework/Model/TeamStatsHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Framework.Model 5 | { 6 | public class TeamStatsHistory 7 | { 8 | [JsonProperty("assists")] 9 | public int Assists { get; set; } 10 | 11 | [JsonProperty("championIds")] 12 | public int[] ChampionIds { get; set; } 13 | 14 | [JsonProperty("deaths")] 15 | public int Deaths { get; set; } 16 | 17 | [JsonProperty("game")] 18 | public Guid Game { get; set; } 19 | 20 | [JsonProperty("id")] 21 | public Guid Id { get; set; } 22 | 23 | [JsonProperty("kills")] 24 | public int Kills { get; set; } 25 | 26 | [JsonProperty("match")] 27 | public Guid Match { get; set; } 28 | 29 | [JsonProperty("opponent")] 30 | public int OpponentId { get; set; } 31 | 32 | [JsonProperty("team")] 33 | public int TeamId { get; set; } 34 | 35 | [JsonProperty("timestamp")] 36 | public double Timestamp { get; set; } 37 | 38 | [JsonProperty("win")] 39 | public bool Win { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Esports/Framework/Model/TeamStatsSummary.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Framework.Model 4 | { 5 | public class TeamStatsSummary 6 | { 7 | [JsonProperty("averageDamageByPosition")] 8 | public dynamic AvgDmgByPosition { get; set; } 9 | 10 | [JsonProperty("averageWinLength")] 11 | public int AvgWinLength { get; set; } 12 | 13 | [JsonProperty("averageWinLengthRank")] 14 | public int AvgWinLengthRank { get; set; } 15 | 16 | [JsonProperty("firstDragonKillRatio")] 17 | public double FirstDragonKillRatio { get; set; } 18 | 19 | [JsonProperty("firstDragonKillRatioRank")] 20 | public int FirstDragonKillRatioRank { get; set; } 21 | 22 | [JsonProperty("firstTowerRatio")] 23 | public double FirstTowerRatio { get; set; } 24 | 25 | [JsonProperty("firstTowerRatioRank")] 26 | public int FirstTowerRatioRank { get; set; } 27 | 28 | [JsonProperty("kdaRatio")] 29 | public double KdaRatio { get; set; } 30 | 31 | [JsonProperty("kdaRatioRank")] 32 | public int KdaRatioRank { get; set; } 33 | 34 | [JsonProperty("teamId")] 35 | public string TeamId { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Esports/Framework/Selenium/Driver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using OpenQA.Selenium; 4 | using OpenQA.Selenium.Interactions; 5 | using OpenQA.Selenium.Support.Extensions; 6 | using OpenQA.Selenium.Support.UI; 7 | 8 | namespace Framework.Selenium 9 | { 10 | public static class Driver 11 | { 12 | [ThreadStatic] static IWebDriver _driver; 13 | 14 | [ThreadStatic] public static WebDriverWait Wait; 15 | 16 | [ThreadStatic] public static WindowManager Window; 17 | 18 | public static void Init(string type, string browser, int setWait) 19 | { 20 | _driver = DriverFactory.Build(type, browser); 21 | Wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(setWait)); 22 | Window = new WindowManager(); 23 | Window.Maximize(); 24 | } 25 | 26 | /// 27 | /// Gets the current instance of IWebDriver. 28 | /// 29 | public static IWebDriver Current => 30 | _driver ?? throw new Exception("_driver is null. Call Driver.Init() first."); 31 | 32 | /// 33 | /// Gets the title of the current page. 34 | /// 35 | public static string Title => Current.Title; 36 | 37 | /// 38 | /// Drags an element from it's current position to the location of another element. 39 | /// 40 | /// Drag element. 41 | /// Drop element. 42 | public static void DragAndDrop(Element dragElement, Element dropOnElement) 43 | { 44 | var actions = new Actions(Current); 45 | actions.DragAndDrop(dragElement.Current, dropOnElement.Current); 46 | actions.Build().Perform(); 47 | } 48 | 49 | /// 50 | /// Navigates to the specified url. 51 | /// 52 | /// URL must start with https or http protocol. 53 | public static void Goto(string url) 54 | { 55 | Current.Navigate().GoToUrl(url); 56 | } 57 | 58 | /// 59 | /// Finds the element based on the locator provided. 60 | /// 61 | /// FindBy mechanism. 62 | /// Name of element for logging purposes. 63 | public static Element FindElement(By by, string elementName = "") 64 | { 65 | var element = Wait.Until(drvr => drvr.FindElement(by)); 66 | return new Element(element, elementName); 67 | } 68 | 69 | /// 70 | /// Finds the elements based on the locator provided. 71 | /// 72 | /// FindBy mechanism. 73 | public static Elements FindElements(By by) 74 | { 75 | var elements = Current.FindElements(by); 76 | return new Elements(elements); 77 | } 78 | 79 | /// 80 | /// Closes all tabs and windows of current instance and disposes unused resources. 81 | /// 82 | public static void Quit() 83 | { 84 | if (_driver != null) 85 | { 86 | Current.Quit(); 87 | } 88 | } 89 | 90 | /// 91 | /// Refreshes the current page. 92 | /// 93 | public static void Refresh() 94 | { 95 | Current.Navigate().Refresh(); 96 | } 97 | 98 | /// 99 | /// Selects the dropdown option. This only works for 'select' elements. 100 | /// 101 | /// Select option by: INDEX | TEXT | VALUE. 102 | /// The Select element. 103 | /// Value to select the option. 104 | public static void SelectDropdownOption(DropdownBy by, Element element, dynamic value) 105 | { 106 | var dropdown = new SelectElement(element.Current); 107 | 108 | switch (by) 109 | { 110 | case DropdownBy.INDEX: 111 | dropdown.SelectByIndex((int)value); 112 | break; 113 | case DropdownBy.TEXT: 114 | dropdown.SelectByText((string)value); 115 | break; 116 | case DropdownBy.VALUE: 117 | dropdown.SelectByValue((string)value); 118 | break; 119 | } 120 | } 121 | 122 | /// 123 | /// Takes a screenshot of the current page as a .png file. 124 | /// Example: Driver.TakeScreenshot("~/pics/ss", "example") 125 | /// This saves the screenshot as ~/pics/ss/example.png 126 | /// 127 | /// Directory to save the file to. 128 | /// Image name without .png extension. 129 | public static void TakeScreenshot(string directory, string imgName) 130 | { 131 | var ss = ((ITakesScreenshot)Current).GetScreenshot(); 132 | var ssFileName = Path.Combine(directory, imgName); 133 | ss.SaveAsFile($"{ssFileName}.png", ScreenshotImageFormat.Png); 134 | } 135 | 136 | /// 137 | /// Takes a screenshot of the current page as a .png file and 138 | /// saves it in the current test's auto-generated directory. 139 | /// 140 | /// Image name without .png extension. 141 | public static void TakeScreenshot(string imgName) 142 | { 143 | var ss = ((ITakesScreenshot)Current).GetScreenshot(); 144 | var ssFileName = Path.Combine("", imgName); 145 | ss.SaveAsFile($"{ssFileName}.png", ScreenshotImageFormat.Png); 146 | } 147 | 148 | /// 149 | /// Executes the javascript against the current page and returns an object. 150 | /// Access the args by indexing into the "arguments" array. 151 | /// Example: var five = Driver.ExecuteScript<int>("return 3 + arguments[0]", 2); 152 | /// 153 | /// An object of type T. 154 | /// Javascript to execute. 155 | /// Arguments to be passed to script. 156 | /// The return type of the script. 157 | public static T ExecuteScript(string js, params object[] args) 158 | { 159 | return Current.ExecuteJavaScript(js, args); 160 | } 161 | 162 | /// 163 | /// Executes the javascript against the current page. 164 | /// Access the args by indexing into the "arguments" array. 165 | /// Example: Driver.ExecuteScript("console.log(arguments[0])", "foo"); 166 | /// 167 | /// Javascript to execute. 168 | /// Arguments to be passed to script. 169 | public static void ExecuteScript(string js, params object[] args) 170 | { 171 | Current.ExecuteJavaScript(js, args); 172 | } 173 | } 174 | 175 | public enum DropdownBy 176 | { 177 | INDEX = 0, 178 | TEXT = 1, 179 | VALUE = 2 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /Esports/Framework/Selenium/DriverFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenQA.Selenium; 3 | using OpenQA.Selenium.Chrome; 4 | using OpenQA.Selenium.Firefox; 5 | using OpenQA.Selenium.Remote; 6 | 7 | namespace Framework.Selenium 8 | { 9 | public static class DriverFactory 10 | { 11 | public static IWebDriver Build(string type, string browser) 12 | { 13 | if (type == "local") 14 | { 15 | switch (browser) 16 | { 17 | case "chrome": 18 | return BuildChromeDriver(); 19 | 20 | case "firefox": 21 | return BuildFirefoxDriver(); 22 | 23 | default: 24 | throw new ArgumentException($"{browser} is not supported locally."); 25 | } 26 | } 27 | 28 | else if (type == "remote") 29 | { 30 | return BuildRemoteDriver(browser); 31 | } 32 | 33 | else 34 | { 35 | throw new ArgumentException($"{type} is invalid. Choose 'local' or 'remote'."); 36 | } 37 | } 38 | 39 | 40 | private static RemoteWebDriver BuildRemoteDriver(string browser) 41 | { 42 | var DOCKER_GRID_HUB_URI = new Uri("http://localhost:4444/wd/hub"); 43 | 44 | RemoteWebDriver driver; 45 | 46 | switch (browser) 47 | { 48 | case "chrome": 49 | var chromeOptions = new ChromeOptions 50 | { 51 | BrowserVersion = "", 52 | PlatformName = "LINUX", 53 | }; 54 | 55 | chromeOptions.AddArgument("--start-maximized"); 56 | 57 | driver = new RemoteWebDriver(DOCKER_GRID_HUB_URI, chromeOptions.ToCapabilities()); 58 | break; 59 | 60 | case "firefox": 61 | var firefoxOptions = new FirefoxOptions 62 | { 63 | BrowserVersion = "", 64 | PlatformName = "LINUX", 65 | }; 66 | 67 | driver = new RemoteWebDriver(DOCKER_GRID_HUB_URI, firefoxOptions.ToCapabilities()); 68 | break; 69 | 70 | default: 71 | throw new ArgumentException($"{browser} is not supported remotely."); 72 | } 73 | 74 | return driver; 75 | } 76 | 77 | private static ChromeDriver BuildChromeDriver() 78 | { 79 | var options = new ChromeOptions(); 80 | options.AddArgument("--start-maximized"); 81 | 82 | return new ChromeDriver(FW.WORKSPACE_DIRECTORY + PlatformDriver, options); 83 | } 84 | 85 | private static FirefoxDriver BuildFirefoxDriver() 86 | { 87 | return new FirefoxDriver(FW.WORKSPACE_DIRECTORY + PlatformDriver); 88 | } 89 | 90 | private static string PlatformDriver 91 | { 92 | get 93 | { 94 | return Environment.OSVersion.Platform.ToString().Contains("Win") 95 | ? "_drivers/windows" 96 | : "_drivers/mac"; 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Esports/Framework/Selenium/Element.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using OpenQA.Selenium; 6 | using OpenQA.Selenium.Interactions; 7 | 8 | namespace Framework.Selenium 9 | { 10 | public class Element 11 | { 12 | public Element(IWebElement element) 13 | { 14 | Current = element; 15 | } 16 | 17 | public Element(IWebElement element, string name) 18 | { 19 | Current = element; 20 | Name = name; 21 | } 22 | 23 | /// 24 | /// Gets the current instance of . 25 | /// 26 | public IWebElement Current { get; } 27 | 28 | /// 29 | /// Gets or sets the mechanism used 30 | /// to find this . 31 | /// 32 | /// The mechanism used to find this element. 33 | public By FoundBy { get; set; } 34 | 35 | /// 36 | /// Gets or sets the name of this . 37 | /// This is used for logging. 38 | /// 39 | /// The name of this element. 40 | public string Name { get; set; } 41 | 42 | /// 43 | /// Gets the text within this element. 44 | /// This is equivalent to using .innerText on an element in javascript. 45 | /// 46 | /// The text of this element. 47 | public string Text => Current.Text; 48 | 49 | /// 50 | /// Gets the tag name of this element. 51 | /// 52 | /// The tag name of this element. 53 | public string TagName => Current.TagName; 54 | 55 | /// 56 | /// Gets the input value of this element. 57 | /// This is equivalent to using .value on an element in javascript. 58 | /// 59 | /// The input value of this element. 60 | public string InputValue => GetAttribute("value"); 61 | 62 | /// 63 | /// Gets a value indicating whether this element is displayed. 64 | /// This means that it is present on the DOM, but not necessarily visible. 65 | /// 66 | /// true if displayed; otherwise, false. 67 | public bool Displayed => Current.Displayed; 68 | 69 | /// 70 | /// Gets a value indicating whether this element is enabled. 71 | /// 72 | /// true if enabled; otherwise, false. 73 | public bool Enabled => Current.Enabled; 74 | 75 | /// 76 | /// Gets a value indicating whether this element is clickable. 77 | /// It must be visible and enabled, meaning that it has a size greater than zero pixels. 78 | /// 79 | /// true if clickable; otherwise, false. 80 | public bool Clickable => Displayed 81 | && Enabled 82 | && Size.Height > 0 83 | && Size.Width > 0; 84 | 85 | /// 86 | /// Gets the value of the specified attribute on this element. 87 | /// Example: <a id="foo"/> -- element.GetAttribute("id"); 88 | /// 89 | /// The value of the attribute. 90 | /// Name of the attribute. This is case-sensitive. 91 | public string GetAttribute(string attrName) 92 | { 93 | return Current.GetAttribute(attrName); 94 | } 95 | 96 | /// 97 | /// Simulates typing text into the element. 98 | /// This can also be used upload files by passing in the URL/path to the file(s). 99 | /// 100 | /// The text to enter into this element. 101 | public void SendKeys(object text) 102 | { 103 | var str = text.ToString(); 104 | Console.WriteLine(string.IsNullOrEmpty(Name) 105 | ? $"Enter '{str}' into input field" 106 | : $"Enter '{str}' into {Name}"); 107 | 108 | try 109 | { 110 | if (string.IsNullOrEmpty(InputValue)) 111 | { 112 | Current.SendKeys(str); 113 | } 114 | else if (InputValue == str) 115 | { 116 | // Do nothing since the expected string is already there 117 | } 118 | else 119 | { 120 | Clear(); 121 | Current.SendKeys(str); 122 | } 123 | } 124 | catch (Exception e) 125 | { 126 | Driver.TakeScreenshot($"failed_to_sendkeys_{Name.Replace(' ', '_')}"); 127 | throw e; 128 | } 129 | } 130 | 131 | /// 132 | /// Clicks this element. 133 | /// 134 | public void Click() 135 | { 136 | Console.WriteLine(string.IsNullOrEmpty(Name) 137 | ? $"Click element" 138 | : $"Click on {Name}"); 139 | 140 | var tries = 0; 141 | 142 | while (tries < 3) 143 | { 144 | try 145 | { 146 | Current.Click(); 147 | break; 148 | } 149 | catch (Exception e) 150 | { 151 | tries++; 152 | 153 | if (e.Message.Contains("Other element would receive the click")) 154 | { 155 | continue; 156 | } 157 | else 158 | { 159 | Driver.TakeScreenshot($"failed_to_click_{Name.Replace(' ', '_')}"); 160 | throw e; 161 | } 162 | } 163 | } 164 | } 165 | 166 | /// 167 | /// Simulates hovering the element with your mouse. 168 | /// 169 | public void Hover() 170 | { 171 | var actions = new Actions(Driver.Current); 172 | actions.MoveToElement(Current); 173 | actions.Build().Perform(); 174 | } 175 | 176 | /// 177 | /// Clears the input value or content of this element. 178 | /// 179 | public void Clear() 180 | { 181 | Current.Clear(); 182 | } 183 | 184 | /// 185 | /// Submits this element to the web server. 186 | /// 187 | public void Submit() 188 | { 189 | Current.Submit(); 190 | } 191 | 192 | /// 193 | /// Gets the parent of this element. 194 | /// 195 | /// The parent element. 196 | public Element Parent 197 | { 198 | get 199 | { 200 | var js = "return arguments[0].parentElement"; 201 | var parent = Driver.ExecuteScript(js, Current); 202 | return new Element(parent); 203 | } 204 | } 205 | 206 | /// 207 | /// Gets an object of the children of this element. 208 | /// 209 | /// The child elements. 210 | public Elements Children 211 | { 212 | get 213 | { 214 | var js = "var children = arguments[0].children; " + 215 | "var array = []; " + 216 | "for (var i = 0; i < children.length; i++) { array.push(children[i]); } " + 217 | "return array;"; 218 | var children = Driver.ExecuteScript>(js, Current); 219 | 220 | return new Elements(children.ToList()); 221 | } 222 | } 223 | 224 | /// 225 | /// Gets an object of the siblings of this element. 226 | /// 227 | /// The sibling elements. 228 | public Elements Siblings 229 | { 230 | get 231 | { 232 | var js = "var children = arguments[0].parentElement.children; " + 233 | "var array = []; " + 234 | "for (var i = 0; i < children.length; i++) { array.push(children[i]); } " + 235 | "return array;"; 236 | var siblings = Driver.ExecuteScript>(js, Current); 237 | 238 | return new Elements(siblings.ToList()); 239 | } 240 | } 241 | 242 | /// 243 | /// Gets a value indicating whether this element is selected. Great for checkboxes or switches. 244 | /// 245 | /// true if selected; otherwise, false. 246 | public bool Selected => Current.Selected; 247 | 248 | /// 249 | /// Gets a object containing the coordinates of the upper-left corner 250 | /// of this element relative to the upper-left corner of the page. 251 | /// 252 | /// The coordinates of this element. 253 | public Point Location => Current.Location; 254 | 255 | /// 256 | /// Gets the object containing the height and width of this element. 257 | /// 258 | /// The size of the element. 259 | public Size Size => Current.Size; 260 | 261 | /// 262 | /// Gets the value of a javascript property of this element. 263 | /// 264 | /// The property value. 265 | /// Property name to get the value from. 266 | public string GetProperty(string propertyName) 267 | { 268 | return Current.GetProperty(propertyName); 269 | } 270 | 271 | /// 272 | /// Gets the value of a CSS property of this element. 273 | /// 274 | /// The CSS property's value. 275 | /// CSS property name. 276 | public string GetCssValue(string cssProperty) 277 | { 278 | return Current.GetCssValue(cssProperty); 279 | } 280 | 281 | /// 282 | /// Finds the first using the 283 | /// mechanism given. 284 | /// 285 | /// The first element found. 286 | /// mechanism. 287 | public Element FindElement(By by) 288 | { 289 | return new Element(Current.FindElement(by)) 290 | { 291 | FoundBy = by 292 | }; 293 | } 294 | 295 | /// 296 | /// Finds the first using the 297 | /// mechanism given. 298 | /// 299 | /// The first element found. 300 | /// mechanism. 301 | /// Name to assign the element. 302 | public Element FindElement(By by, string name) 303 | { 304 | return new Element(Current.FindElement(by)) 305 | { 306 | FoundBy = by, 307 | Name = name 308 | }; 309 | } 310 | 311 | /// 312 | /// Finds all using the 313 | /// mechanism given. 314 | /// 315 | /// The elements found. 316 | /// mechanism. 317 | public Elements FindElements(By by) 318 | { 319 | var elements = new Elements(Current.FindElements(by)) 320 | { 321 | FoundBy = by 322 | }; 323 | 324 | return elements; 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /Esports/Framework/Selenium/Elements.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using OpenQA.Selenium; 4 | 5 | namespace Framework.Selenium 6 | { 7 | public class Elements : IList 8 | { 9 | readonly IList _list; 10 | readonly List _elements; 11 | 12 | public Elements(IList list) 13 | { 14 | _list = list; 15 | _elements = new List(); 16 | 17 | foreach (var element in list) 18 | { 19 | _elements.Add(new Element(element)); 20 | } 21 | } 22 | 23 | public Element this[int index] 24 | { 25 | get => _elements[index]; 26 | set => _elements[index] = value; 27 | } 28 | 29 | public By FoundBy { get; set; } 30 | 31 | public bool IsEmpty => Count == 0; 32 | 33 | public int Count => _elements.Count; 34 | 35 | public bool IsReadOnly => _list.IsReadOnly; 36 | 37 | public void Add(Element element) 38 | { 39 | _elements.Add(element); 40 | } 41 | 42 | public void Clear() 43 | { 44 | _elements.Clear(); 45 | } 46 | 47 | public bool Contains(Element element) 48 | { 49 | return _elements.Contains(element); 50 | } 51 | 52 | public void CopyTo(Element[] array, int arrayIndex) 53 | { 54 | _elements.CopyTo(array, arrayIndex); 55 | } 56 | 57 | public IEnumerator GetEnumerator() 58 | { 59 | return _elements.GetEnumerator(); 60 | } 61 | 62 | public int IndexOf(Element element) 63 | { 64 | return _elements.IndexOf(element) + 1; 65 | } 66 | 67 | public void Insert(int index, Element element) 68 | { 69 | _elements.Insert(index, element); 70 | } 71 | 72 | public bool Remove(Element element) 73 | { 74 | return _elements.Remove(element); 75 | } 76 | 77 | public void RemoveAt(int index) 78 | { 79 | _elements.RemoveAt(index); 80 | } 81 | 82 | IEnumerator IEnumerable.GetEnumerator() 83 | { 84 | return GetEnumerator(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Esports/Framework/Selenium/WindowManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Drawing; 4 | 5 | namespace Framework.Selenium 6 | { 7 | public class WindowManager 8 | { 9 | public ReadOnlyCollection CurrentWindows => Driver.Current.WindowHandles; 10 | 11 | public Size ScreenSize 12 | { 13 | get 14 | { 15 | var js = "var dimensions = [];" + 16 | "dimensions.push(window.screen.availWidth);" + 17 | "dimensions.push(window.screen.availHeight);" + 18 | "return dimensions;"; 19 | var dimensions = Driver.ExecuteScript>(js, null); 20 | var x = Convert.ToInt32(dimensions[0]); 21 | var y = Convert.ToInt32(dimensions[1]); 22 | 23 | return new Size(x, y); 24 | } 25 | } 26 | 27 | public void SwitchTo(int windowIndex) 28 | { 29 | Driver.Current.SwitchTo().Window(CurrentWindows[windowIndex]); 30 | } 31 | 32 | public void Maximize() 33 | { 34 | Driver.Current.Manage().Window.Position = new Point(0, 0); 35 | var size = ScreenSize; 36 | Driver.Current.Manage().Window.Size = size; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Esports/Framework/Services/IPlayerStatsService.cs: -------------------------------------------------------------------------------- 1 | using Framework.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Framework.Services 6 | { 7 | public interface IPlayerStatsService 8 | { 9 | List GetAllPlayerStats(string groupName, string tournamentId); 10 | 11 | PlayerStats GetPlayerStatsById(string groupName, string tournamentId, int id); 12 | 13 | PlayerStats GetPlayerStatsByName(string groupName, string tournamentId, string name); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Esports/Framework/Services/LocalPlayerService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using Framework.Model; 5 | using Newtonsoft.Json; 6 | 7 | namespace Framework.Services 8 | { 9 | public class LocalPlayerService 10 | { 11 | public LocalPlayerService() 12 | { 13 | _playerStats = DeserializeLocalJson(); 14 | } 15 | 16 | readonly List _playerStats; 17 | 18 | public List GetAllPlayerStats() 19 | { 20 | return _playerStats; 21 | } 22 | 23 | public PlayerStats GetPlayerStatsById(int id) 24 | { 25 | return _playerStats.FirstOrDefault(p => p.Id == id); 26 | } 27 | 28 | public PlayerStats GetPlayerStatsByName(string name) 29 | { 30 | return _playerStats.FirstOrDefault(p => p.Name == name); 31 | } 32 | 33 | private List DeserializeLocalJson() 34 | { 35 | dynamic json = JsonConvert.DeserializeObject( 36 | File.ReadAllText($"{Directory.GetCurrentDirectory()}/Framework/Data/tournamentPlayerStats.json") 37 | ); 38 | 39 | return json["stats"].ToObject>(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Esports/Framework/Services/PlayerService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Framework.Model; 5 | using Newtonsoft.Json; 6 | using RestSharp; 7 | 8 | namespace Framework.Services 9 | { 10 | public class PlayerService : IPlayerStatsService 11 | { 12 | // player stats parameters: 13 | // groupName(string) 14 | // tournamentId(Guid) 15 | // examples: 16 | // groupName=regular_season 17 | // tournamentId=8531db79-ade3-4294-ae4a-ef639967c393 18 | 19 | string _baseUrl => "https://api.lolesports.com/api/v2"; 20 | string _playerStatsEndpoint => _baseUrl + "/tournamentPlayerStats"; 21 | 22 | public List GetAllPlayerStats(string groupName, string tournamentId) 23 | { 24 | var client = new RestClient(_playerStatsEndpoint); 25 | var request = new RestRequest($"?groupName={groupName}&tournamentId={new Guid(tournamentId)}", Method.GET); 26 | var response = client.Execute(request); 27 | 28 | dynamic json = JsonConvert.DeserializeObject(response.Content); 29 | return json["stats"].ToObject>(); 30 | } 31 | 32 | public PlayerStats GetPlayerStatsById(string groupName, string tournamentId, int id) 33 | { 34 | return GetAllPlayerStats(groupName, tournamentId).FirstOrDefault(p => p.Id == id); 35 | } 36 | 37 | public PlayerStats GetPlayerStatsByName(string groupName, string tournamentId, string name) 38 | { 39 | return GetAllPlayerStats(groupName, tournamentId).FirstOrDefault(p => p.Name == name); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Esports/League.Com/Esports.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Framework.Selenium; 3 | using League.Com.Pages; 4 | 5 | namespace League.Com 6 | { 7 | public static class Esports 8 | { 9 | [ThreadStatic] public static HomePage Home; 10 | 11 | [ThreadStatic] public static ServiceStatusPage ServiceStatus; 12 | 13 | [ThreadStatic] public static StandingsPage Standings; 14 | 15 | [ThreadStatic] public static SupportPage Support; 16 | 17 | [ThreadStatic] public static TicketsPage Tickets; 18 | 19 | public static void Init() 20 | { 21 | Home = new HomePage(); 22 | ServiceStatus = new ServiceStatusPage(); 23 | Standings = new StandingsPage(); 24 | Support = new SupportPage(); 25 | Tickets = new TicketsPage(); 26 | } 27 | 28 | public static void Goto() 29 | { 30 | Driver.Goto("https://nexus.leagueoflegends.com/en-us/esports/"); 31 | Driver.Wait.Until(driver => Home.Map.PlayNowSignupButton.Displayed); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Esports/League.Com/League.Com.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/Base/EsportsMenu.cs: -------------------------------------------------------------------------------- 1 | using Framework.Selenium; 2 | using OpenQA.Selenium; 3 | 4 | namespace League.Com.Pages 5 | { 6 | public class EsportsMenu 7 | { 8 | public Element IssuesDialog => Driver.FindElement(By.CssSelector("a.message-text")); 9 | 10 | public Element IssuesButton => Driver.FindElement(By.Id("riotbar-service-status-icon")); 11 | 12 | public Element StandingsLink => Driver.FindElement(By.XPath("(//a[contains(text(), 'Standings')])[last()]"), "STANDINGS nav link"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/Base/PageBase.cs: -------------------------------------------------------------------------------- 1 | namespace League.Com.Pages.Base 2 | { 3 | public class PageBase 4 | { 5 | public readonly EsportsMenu EsportsMenu; 6 | 7 | public readonly WatchMenu WatchMenu; 8 | 9 | public PageBase() 10 | { 11 | EsportsMenu = new EsportsMenu(); 12 | WatchMenu = new WatchMenu(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/Base/WatchMenu.cs: -------------------------------------------------------------------------------- 1 | using Framework.Selenium; 2 | using OpenQA.Selenium; 3 | 4 | namespace League.Com.Pages.Base 5 | { 6 | public class WatchMenu 7 | { 8 | public Element WatchLink => Driver.FindElement(By.CssSelector(".item.link.watch"), "Watch Nav Link"); 9 | public Element ScheduleLink => Driver.FindElement(By.CssSelector(".item.link.schedule"), "Schedule Nav Link"); 10 | public Element VodsLink => Driver.FindElement(By.CssSelector(".item.link.vods"), "VODS Nav Link"); 11 | public Element StandingsLink => Driver.FindElement(By.CssSelector(".item.link.standings"), "Standings Nav Link"); 12 | public Element RewardsLink => Driver.FindElement(By.CssSelector(".item.link.rewards"), "Rewards Nav Link"); 13 | public Element NewsLink => Driver.FindElement(By.CssSelector(".item.link.news"), "News Nav Link"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/HomePage.cs: -------------------------------------------------------------------------------- 1 | using Framework.Selenium; 2 | using League.Com.Pages.Base; 3 | using OpenQA.Selenium; 4 | 5 | namespace League.Com.Pages 6 | { 7 | public class HomePage : PageBase 8 | { 9 | public readonly HomePageMap Map; 10 | 11 | public HomePage() 12 | { 13 | Map = new HomePageMap(); 14 | } 15 | } 16 | 17 | public class HomePageMap 18 | { 19 | public Element PlayNowSignupButton => Driver.FindElement(By.CssSelector("[data-riotbar-link-id='signup']")); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/ServiceStatusPage.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Framework.Selenium; 3 | using OpenQA.Selenium; 4 | 5 | namespace League.Com.Pages 6 | { 7 | public class ServiceStatusPage 8 | { 9 | public readonly ServiceStatusPageMap Map; 10 | 11 | public ServiceStatusPage() 12 | { 13 | Map = new ServiceStatusPageMap(); 14 | } 15 | 16 | public void Goto() 17 | { 18 | Driver.Wait.Until(driver => driver.FindElement(By.XPath("(//a[@data-riotbar-link-id='service-status'])[last()]"))); 19 | Map.ServiceStatusLink.Click(); 20 | } 21 | 22 | public bool ServiceOnline(string serviceName) // Game, Client 23 | { 24 | var service = Map.Services.First(ser => 25 | ser.FindElement(By.CssSelector(".service-name")) 26 | .Text == serviceName); 27 | 28 | var status = service.FindElement(By.CssSelector(".status-text")); 29 | 30 | return status.Text == "Online"; 31 | } 32 | } 33 | 34 | public class ServiceStatusPageMap 35 | { 36 | public Element ServiceStatusLink => Driver.FindElement(By.XPath("(//a[@data-riotbar-link-id='service-status'])[last()]")); 37 | 38 | public Elements Services => Driver.FindElements(By.CssSelector(".service-info")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/StandingsPage.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Framework.Model; 3 | using Framework.Selenium; 4 | using League.Com.Pages.Base; 5 | using OpenQA.Selenium; 6 | 7 | namespace League.Com.Pages 8 | { 9 | public class StandingsPage : PageBase 10 | { 11 | public readonly StandingsPageMap Map; 12 | 13 | public StandingsPage() 14 | { 15 | Map = new StandingsPageMap(); 16 | } 17 | 18 | public void Goto() 19 | { 20 | Driver.Goto("https://watch.na.lolesports.com/standings"); 21 | Driver.Wait.Until(drvr => Map.FirstLeague.Clickable); 22 | } 23 | 24 | public void SwitchTo(string league, string stage) 25 | { 26 | Map.LeagueByName(league).Click(); 27 | Map.StageByName(stage).Click(); 28 | 29 | Driver.Wait.Until(driver => Map.FirstRow.Displayed); 30 | } 31 | 32 | public TeamStanding FirstPlace => _generateTeamStandingFromRow(Map.FirstRow); 33 | 34 | public TeamStanding GetTeamByName(string name) 35 | { 36 | var row = Map.TeamRows.FirstOrDefault(r => r.Text.Contains(name)); 37 | return _generateTeamStandingFromRow(row); 38 | } 39 | 40 | public bool ResultsDisplayed() 41 | { 42 | var result = false; 43 | 44 | foreach (var row in Map.TeamRows) 45 | { 46 | var rank = row.FindElement(By.CssSelector(".ordinal")); 47 | var name = row.FindElement(By.CssSelector(".team .name")); 48 | var record = row.FindElement(By.CssSelector(".team .record")); 49 | 50 | if (!rank.Displayed || !name.Displayed || !record.Displayed) 51 | { 52 | result = false; 53 | break; 54 | } 55 | 56 | result = true; 57 | } 58 | 59 | return result; 60 | } 61 | 62 | private TeamStanding _generateTeamStandingFromRow(Element row) 63 | { 64 | return new TeamStanding 65 | { 66 | Rank = int.Parse(row.FindElement(By.CssSelector(".ordinal")).Text), 67 | Name = row.FindElement(By.CssSelector(".team .name")).Text, 68 | Record = row.FindElement(By.CssSelector(".team .record")).Text 69 | }; 70 | } 71 | } 72 | 73 | public class StandingsPageMap 74 | { 75 | // Leagues 76 | public Element FirstLeague => Driver.FindElement(By.CssSelector(".league"), "First League"); 77 | public Elements Leagues => Driver.FindElements(By.CssSelector(".league")); 78 | 79 | public Element LeagueByName(string name) => Driver.FindElement( 80 | by: By.XPath($"//div[@class='name' and text()='{name}']"), 81 | elementName: $"{name} League Filter"); 82 | 83 | public Element StageByName(string name) => Driver.FindElement( 84 | by: By.XPath($"//div[@class='stage-option' and text()='{name}']"), 85 | elementName: $"{name} Stage"); 86 | 87 | // Teams 88 | public Element FirstRow => Driver.FindElement(By.CssSelector(".ranking"), "First Team"); 89 | public Elements TeamRows => Driver.FindElements(By.CssSelector(".ranking")); 90 | 91 | // used if screen is too small 92 | public Element LeagueSelector => Driver.FindElement(By.CssSelector(".league-selector")); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/SupportPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Framework.Selenium; 3 | using OpenQA.Selenium; 4 | 5 | namespace League.Com.Pages 6 | { 7 | public class SupportPage 8 | { 9 | public readonly SupportPageMap Map; 10 | 11 | public SupportPage() 12 | { 13 | Map = new SupportPageMap(); 14 | } 15 | 16 | public void Goto() 17 | { 18 | Map.ExploreMenu.Hover(); 19 | Map.SupportLink.Click(); 20 | } 21 | } 22 | 23 | public class SupportPageMap 24 | { 25 | public Element ExploreMenu => Driver.FindElement(By.CssSelector(".riotbar-left-content")); 26 | 27 | public Element SupportLink => Driver.FindElement(By.CssSelector("[data-riotbar-link-id='support']")); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Esports/League.Com/Pages/TicketsPage.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using Framework.Selenium; 3 | using OpenQA.Selenium; 4 | 5 | namespace League.Com.Pages 6 | { 7 | public class TicketsPage 8 | { 9 | public readonly TicketsPageMap Map; 10 | 11 | public TicketsPage() 12 | { 13 | Map = new TicketsPageMap(); 14 | } 15 | 16 | public void Goto() 17 | { 18 | Driver.Wait.Until(driver => Map.TicketsLink.Displayed); 19 | Map.TicketsLink.Click(); 20 | } 21 | 22 | public void ClickFirstDate() 23 | { 24 | Map.FirstDate.Click(); 25 | } 26 | 27 | public bool SoldOut() 28 | { 29 | Driver.Wait.Until(driver => Map.TicketContainer.Displayed); 30 | 31 | if (Map.SoldOut.Displayed && !Map.AddToCartButton.Enabled) 32 | { 33 | return true; 34 | } 35 | else 36 | { 37 | return false; 38 | } 39 | } 40 | } 41 | 42 | public class TicketsPageMap 43 | { 44 | public Element TicketsLink => Driver.FindElement(By.XPath("(//a[contains(text(), 'Tickets')])[last()]")); 45 | public Element FirstDate => Driver.FindElement(By.CssSelector(".squadup-checkout-event-box")); 46 | 47 | public Element TicketContainer => Driver.FindElement(By.CssSelector(".form-group")); 48 | public Element SoldOut => Driver.FindElement(By.XPath("//*[text()='Sold Out!']")); 49 | public Element AddToCartButton => Driver.FindElement(By.XPath("//span[text()='Add to Cart']/parent::*")); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Esports/Tests/Base/TestBase.cs: -------------------------------------------------------------------------------- 1 | using Framework; 2 | using Framework.Selenium; 3 | using League.Com; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.Base 7 | { 8 | public abstract class TestBase 9 | { 10 | protected TestBase() 11 | { 12 | FW.Init(); 13 | } 14 | 15 | [SetUp] 16 | public virtual void Setup() 17 | { 18 | Driver.Init( 19 | type: FW.Config.Driver.Type, 20 | browser: FW.Config.Driver.Browser, 21 | setWait: FW.Config.Driver.Wait 22 | ); 23 | 24 | Esports.Init(); 25 | } 26 | 27 | [TearDown] 28 | public virtual void Cleanup() 29 | { 30 | Driver.Quit(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Esports/Tests/League.Com.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Esports/Tests/Services.cs: -------------------------------------------------------------------------------- 1 | using Framework.Services; 2 | using NUnit.Framework; 3 | 4 | namespace League.Com.Tests 5 | { 6 | [TestFixture] 7 | public class Services 8 | { 9 | [TestCase("regular_season")] 10 | [TestCase("playoffs")] 11 | [TestCase("regionals")] 12 | [Test, Category("unit"), Category("1")] 13 | public void Player_service_can_get_players(string stage) 14 | { 15 | IPlayerStatsService service = new PlayerService(); 16 | Assert.IsNotNull(service.GetAllPlayerStats(stage, "8531db79-ade3-4294-ae4a-ef639967c393")); 17 | } 18 | 19 | [Test, Category("unit"), Category("2")] 20 | public void Player_service_can_get_player_by_id() 21 | { 22 | IPlayerStatsService service = new PlayerService(); 23 | var player = service.GetPlayerStatsById("regular_season", "8531db79-ade3-4294-ae4a-ef639967c393", id: 60); 24 | 25 | Assert.AreEqual("Bjergsen", player.Name); 26 | } 27 | 28 | [Test, Category("unit"), Category("3")] 29 | public void Player_service_can_get_player_by_name() 30 | { 31 | IPlayerStatsService service = new PlayerService(); 32 | var player = service.GetPlayerStatsByName( 33 | groupName: "regular_season", 34 | tournamentId: "8531db79-ade3-4294-ae4a-ef639967c393", 35 | name: "Bjergsen" 36 | ); 37 | 38 | Assert.AreEqual(60, player.Id); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Esports/Tests/Standings.cs: -------------------------------------------------------------------------------- 1 | using Framework.Constants; 2 | using NUnit.Framework; 3 | using Tests.Base; 4 | 5 | namespace League.Com.Tests 6 | { 7 | [TestFixture, Parallelizable] 8 | public class Standings : TestBase 9 | { 10 | [SetUp] 11 | public override void Setup() 12 | { 13 | base.Setup(); 14 | Esports.Standings.Goto(); 15 | } 16 | 17 | [Test, Parallelizable] 18 | [Category("standings"), Category("demo"), Category("lcs")] 19 | public void Filter_by_LCS_region() 20 | { 21 | Esports.Standings.SwitchTo(Leagues.LCS, "Regular Season"); 22 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Team Liquid")); 23 | } 24 | 25 | [Test, Parallelizable] 26 | [Category("standings"), Category("demo")] 27 | public void Filter_by_LEC_region() 28 | { 29 | Esports.Standings.SwitchTo(Leagues.LEC, "Regular Season"); 30 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("G2 Esports")); 31 | } 32 | 33 | [Test, Parallelizable] 34 | [Category("standings")] 35 | public void Filter_by_LCK_region() 36 | { 37 | Esports.Standings.SwitchTo(Leagues.LCK, "Regular Season"); 38 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Griffin")); 39 | } 40 | 41 | [Test, Parallelizable] 42 | [Category("standings")] 43 | public void Filter_by_LPL_region() 44 | { 45 | Esports.Standings.SwitchTo(Leagues.LPL, "Regular Season"); 46 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("FunPlus Phoenix")); 47 | } 48 | 49 | [Test, Parallelizable] 50 | [Category("standings")] 51 | public void Filter_by_LCS_academy() 52 | { 53 | Esports.Standings.SwitchTo(Leagues.LCS_ACADEMY, "Regular Season"); 54 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Cloud9 Academy")); 55 | } 56 | 57 | [Test, Parallelizable] 58 | [Category("standings")] 59 | public void Filter_by_TCL_region() 60 | { 61 | Esports.Standings.SwitchTo(Leagues.TCL, "Regular Season"); 62 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Royal Youth")); 63 | } 64 | 65 | [Test, Parallelizable] 66 | [Category("standings")] 67 | public void Filter_by_CBLOL_region() 68 | { 69 | Esports.Standings.SwitchTo(Leagues.CBLOL, "Regular Season"); 70 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Flamengo eSports")); 71 | } 72 | 73 | [Test, Parallelizable] 74 | [Category("standings")] 75 | public void Filter_by_LLA_region() 76 | { 77 | Esports.Standings.SwitchTo(Leagues.LLA, "Regular Season"); 78 | Assert.That(Esports.Standings.FirstPlace.Name.Equals("Isurus Gaming")); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Esports/Tests/Support.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Framework.Selenium; 3 | using NUnit.Framework; 4 | using Tests.Base; 5 | 6 | namespace League.Com.Tests 7 | { 8 | [TestFixture, Parallelizable] 9 | public class Support : TestBase 10 | { 11 | [Test, Parallelizable, Category("support")] 12 | public void Game_service_online() 13 | { 14 | Esports.Goto(); 15 | Esports.Support.Goto(); 16 | Esports.ServiceStatus.Goto(); 17 | Assert.That(Esports.ServiceStatus.ServiceOnline("Game")); 18 | } 19 | 20 | [Test, Parallelizable, Category("support")] 21 | public void Client_service_online() 22 | { 23 | Esports.Goto(); 24 | Esports.Support.Goto(); 25 | Esports.ServiceStatus.Goto(); 26 | Assert.That(Esports.ServiceStatus.ServiceOnline("Client")); 27 | } 28 | 29 | [Test, Parallelizable, Category("support")] 30 | public void Store_service_online() 31 | { 32 | Esports.Goto(); 33 | Esports.Support.Goto(); 34 | Esports.ServiceStatus.Goto(); 35 | Assert.That(Esports.ServiceStatus.ServiceOnline("Store")); 36 | } 37 | 38 | [Test, Parallelizable, Category("support")] 39 | public void Website_service_online() 40 | { 41 | Esports.Goto(); 42 | Esports.Support.Goto(); 43 | Esports.ServiceStatus.Goto(); 44 | Assert.That(Esports.ServiceStatus.ServiceOnline("Website")); 45 | } 46 | 47 | [Test, Parallelizable, Category("support")] 48 | public void Issues_dialog_redirects_to_service_status() 49 | { 50 | Esports.Goto(); 51 | Esports.Home.EsportsMenu.IssuesButton.Click(); 52 | Esports.Home.EsportsMenu.IssuesDialog.Click(); 53 | Assert.That(Driver.Title.Contains("Service Status")); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Esports/Tests/Tickets.cs: -------------------------------------------------------------------------------- 1 | using Framework.Selenium; 2 | using NUnit.Framework; 3 | using OpenQA.Selenium; 4 | using Tests.Base; 5 | 6 | namespace League.Com.Tests 7 | { 8 | [TestFixture] 9 | public class Tickets : TestBase 10 | { 11 | [Test] 12 | public void Goto_test() 13 | { 14 | Driver.Goto("https://google.com"); 15 | Driver.FindElement(By.Name("q")).SendKeys("puppies"); 16 | Driver.FindElement(By.Name("btnK")).Submit(); 17 | } 18 | 19 | [Test] 20 | public void Cannot_buy_sold_out_tickets() 21 | { 22 | // 1. go to lolesports.com 23 | // 2. go to tickets page 24 | // 3. select first date 25 | // 4. validate you can't purchase 26 | // - Add to Cart is disabled 27 | 28 | Esports.Goto(); 29 | Esports.Tickets.Goto(); 30 | Esports.Tickets.ClickFirstDate(); 31 | Assert.IsTrue(Esports.Tickets.SoldOut()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Esports/_drivers/mac/chromedriver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElSnoMan/esports-automation/c0a53eeb098571e06d9efb6e1f1a31f7a70b43e9/Esports/_drivers/mac/chromedriver -------------------------------------------------------------------------------- /Esports/_drivers/windows/chromedriver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElSnoMan/esports-automation/c0a53eeb098571e06d9efb6e1f1a31f7a70b43e9/Esports/_drivers/windows/chromedriver.exe -------------------------------------------------------------------------------- /Esports/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "driver": { 3 | "browser": "chrome", 4 | "type": "local", 5 | "wait": 30 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Esports/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | 4 | selenium-hub: 5 | image: selenium/hub 6 | ports: 7 | - "4444:4444" 8 | environment: 9 | GRID_MAX_SESSION: 16 10 | GRID_BROWSER_TIMEOUT: 300 11 | GRID_TIMEOUT: 300 12 | 13 | chrome: 14 | image: selenium/node-chrome 15 | depends_on: 16 | - selenium-hub 17 | environment: 18 | HUB_PORT_4444_TCP_ADDR: selenium-hub 19 | HUB_PORT_4444_TCP_PORT: 4444 20 | NODE_MAX_SESSION: 2 21 | NODE_MAX_INSTANCES: 2 22 | # volumes: 23 | # - /dev/shm:/dev/shm 24 | 25 | firefox: 26 | image: selenium/node-firefox 27 | depends_on: 28 | - selenium-hub 29 | environment: 30 | HUB_PORT_4444_TCP_ADDR: selenium-hub 31 | HUB_PORT_4444_TCP_PORT: 4444 32 | NODE_MAX_SESSION: 4 33 | NODE_MAX_INSTANCES: 4 34 | -------------------------------------------------------------------------------- /Esports/failed_to_click_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElSnoMan/esports-automation/c0a53eeb098571e06d9efb6e1f1a31f7a70b43e9/Esports/failed_to_click_.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the esports-automation Repo! 2 | This is the repo that is paired with my Test Automation University course called _Scaling Tests with Docker._ 3 | 4 | > IMPORTANT: The repo will look different because it's easier to update the repo than the videos in the course. Please make sure to read the entire README. Thanks :) 5 | 6 | 7 | ## How do I get started? 8 | 1. Download the .NET Core 2.2 or greater SDK and install it on your machine. 9 | - https://dotnet.microsoft.com/download/dotnet-core/2.2 10 | - Click on .NET Core Installer 11 | 12 | 2. Clone or fork this repo and open it in either Visual Studio Community or VS Code. 13 | 14 | 3. Then, in your terminal, make sure to `cd` into the `Esports` directory 15 | 16 | ```bash 17 | $ cd /esports-automation/Esports 18 | ``` 19 | 20 | 4. You're good to go! 21 | 22 | ## Running the tests 23 | 24 | Since the course was recorded, running tests have changed a bit. 25 | 26 | 1. There is no longer a `.runsettings` file in the repo. This has been replaced with the `config.json` and is consumed in the `FW.cs` file. 27 | 28 | 2. How you execute the tests is different too. Check out the commands below. 29 | 30 | 31 | This command is used to run tests with the Category of `demo`. This is currently only two tests in the Standings suite. 32 | 33 | ```bash 34 | $ dotnet test --filter testcategory=demo 35 | ``` 36 | 37 | Similar to the command above, but this is used to run all of the tests in the Standings suite. 38 | 39 | ```bash 40 | $ dotnet test --filter testcategory=standings 41 | ``` 42 | 43 | I put this here in case you wanted to run tests by name. This command run all tests that have `lcs` in their name (not case-sensitive). 44 | 45 | ```bash 46 | $ dotnet test --filter lcs 47 | ``` 48 | 49 | Microsoft's documentation on their `dotnet test` command can be found here: 50 | - https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test 51 | 52 | 53 | ## Questions or Feedback 54 | Please feel free to reach out to me using any of these: 55 | 56 | - LinkedIn: https://www.linkedin.com/in/carlos-kidman/ 57 | - Twitter: @CarlosKidman 58 | - YouTube: https://www.youtube.com/channel/UCNvYBOCETf7MByrYKDTU3fQ 59 | --------------------------------------------------------------------------------