From 1fe6f1505ce0f81421a9177d196c4ac6c2a5b38a Mon Sep 17 00:00:00 2001 From: ili101 Date: Sun, 11 Oct 2020 01:53:25 +0300 Subject: [PATCH 1/5] ConvertTo-Selenium WIP --- ConvertTo-Selenium.ps1 | 114 ++++++++++++++++ Example.side | 295 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 409 insertions(+) create mode 100644 ConvertTo-Selenium.ps1 create mode 100644 Example.side diff --git a/ConvertTo-Selenium.ps1 b/ConvertTo-Selenium.ps1 new file mode 100644 index 0000000..77ad550 --- /dev/null +++ b/ConvertTo-Selenium.ps1 @@ -0,0 +1,114 @@ +Import-Module .\Selenium.psd1 + +function ConvertTo-Selenium { + <# + .SYNOPSIS + Convert Selenium IDE .side recording file to PowerShell commands. + #> + [CmdletBinding()] + param ( + # Path to .side file. + [Parameter(Mandatory)] + [String]$Path + ) + + $ByMap = @{ + id = 'Id' + css = 'CssSelector' + xpath = 'XPath' + linkText = 'LinkText' + label = 'Text' + index = 'Index' + } + function Get-Replace { + <# + .SYNOPSIS + Helper function to convert ScriptBlocks to strings. + + Parameter set 1: + Replace -From [String] with -To [String]. + -QuotesTo adds quotes around -To. + -SplitTo take only the value part from "label=value" of -To. + + Parameter set 2: + -By "label=value" replaces input "-By $By" with "-By -value ''". + * $ByMap is used to map the labels. + #> + [CmdletBinding()] + param ( + [String]$From, + [String]$To, + [String]$By, + [Parameter(ValueFromPipeline)] + $InputObject, + [switch]$QuotesTo, + [switch]$SplitTo + ) + process { + $String = $InputObject.ToString().Trim() + if ($From) { + if ($QuotesTo) { + $To = '"' + $To + '"' + } + if ($SplitTo) { + $To = $To.Split('=', 2)[1] + } + $String = $String.Replace($From, $To) + } + if ($By) { + $String = $String.Replace('-By $By', ('-By {0} -value {1}' -f $ByMap[$By.Split('=', 2)[0]], ('"' + $By.Split('=', 2)[1] + '"'))) + } + $String + } + } + + $ActionMap = @{ + click = { Invoke-SeClick } + sendKeys = { Invoke-SeKeys -Keys $Keys } + type = { Invoke-SeKeys -Keys $Keys } + select = { Set-SeSelectValue -By $By } + } + + $Recording = Get-Content -Path $Path | ConvertFrom-Json + $BaseUrl = [Uri]$Recording.url + '# Project: ' + $Recording.name + + foreach ($Test in $Recording.tests) { + '# Test: ' + $Test.name + foreach ($Command in $Test.commands) { + $PsCode = switch ($Command) { + { $_.comment } { '# Description: ' + $_.comment } + { $_.command -eq 'open' } { + $Url = if ([Uri]::IsWellFormedUriString($_.target, [System.UriKind]::Relative)) { + [Uri]::new($BaseUrl, $_.target) + } + else { + $_.target + } + { Set-SeUrl -Url $Url } | Get-Replace -From '$Url' -To $Url -QuotesTo + Break + } + { $_.command -eq 'close' } { { Stop-SeDriver } ; Break } + { $_.command -in $ActionMap.Keys } { + $Action = $ActionMap[$_.command] | Get-Replace -From '$Keys' -To $_.value -QuotesTo -By $_.value + { Get-SeElement -By $By | _Action_ } | Get-Replace -From '_Action_' -To $Action -By $_.target + Break + } + { $_.command -eq 'selectFrame' } { + if ($_.target -eq 'relative=parent') { + { Switch-SeFrame -Parent } + } + else { + { $null = (Get-SeDriver -Current).SwitchTo().Frame($Index) } | Get-Replace -From '$Index' -To $_.target -SplitTo + } + Break + } + Default { '# Unsupported command. Command: "{0}", Target: "{1}", Value: "{2}", Comment: "{3}".' -f $_.command, $_.target, $_.value, $_.comment } + } + $PsCode | Get-Replace + } + } +} +$PsCode = ConvertTo-Selenium -Path .\Example.side +$null = Start-SeDriver -Browser Chrome +. ([scriptblock]::Create(($PsCode | Out-String))) \ No newline at end of file diff --git a/Example.side b/Example.side new file mode 100644 index 0000000..a11d238 --- /dev/null +++ b/Example.side @@ -0,0 +1,295 @@ +{ + "id": "1a83cd76-e78d-4402-b40e-5805b2403f44", + "version": "2.0", + "name": "Example", + "url": "https://automationintesting.com", + "tests": [{ + "id": "0896d7a2-81f1-4972-bbc3-4880718bc157", + "name": "Test1", + "commands": [{ + "id": "4770f8f0-e2c5-434e-847c-b79ecadebf41", + "comment": "", + "command": "open", + "target": "/selenium/testpage/", + "targets": [], + "value": "" + }, { + "id": "8068ba5f-597f-4d50-834d-b057c2cfb2ea", + "comment": "", + "command": "setWindowSize", + "target": "1936x1056", + "targets": [], + "value": "" + }, { + "id": "90423ff3-98f2-4de8-9998-32088c188647", + "comment": "", + "command": "click", + "target": "id=firstname", + "targets": [ + ["id=firstname", "id"], + ["css=#firstname", "css:finder"], + ["xpath=//input[@id='firstname']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div/div/label/input", "xpath:idRelative"], + ["xpath=//input", "xpath:position"] + ], + "value": "" + }, { + "id": "fa88b63a-f245-4374-8db2-284f239c3ee0", + "comment": "", + "command": "type", + "target": "id=firstname", + "targets": [ + ["id=firstname", "id"], + ["css=#firstname", "css:finder"], + ["xpath=//input[@id='firstname']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div/div/label/input", "xpath:idRelative"], + ["xpath=//input", "xpath:position"] + ], + "value": "test" + }, { + "id": "2626b60b-2f5b-473d-b342-4f5ec36501b5", + "comment": "", + "command": "click", + "target": "xpath=//form[@id='contactus']/div[2]/div/label/input", + "targets": [ + ["id=surname", "id"], + ["css=#surname", "css:finder"], + ["xpath=//input[@id='surname']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[2]/div/label/input", "xpath:idRelative"], + ["xpath=//div[2]/div/label/input", "xpath:position"] + ], + "value": "" + }, { + "id": "65b0a638-0f79-4a43-ac8a-db482d18c3cd", + "comment": "", + "command": "type", + "target": "xpath=//form[@id='contactus']/div[2]/div/label/input", + "targets": [ + ["id=surname", "id"], + ["css=#surname", "css:finder"], + ["xpath=//input[@id='surname']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[2]/div/label/input", "xpath:idRelative"], + ["xpath=//div[2]/div/label/input", "xpath:position"] + ], + "value": "test2" + }, { + "id": "e9047045-ede4-4c13-81b5-f8c036cb527c", + "comment": "", + "command": "click", + "target": "id=gender", + "targets": [ + ["id=gender", "id"], + ["css=#gender", "css:finder"], + ["xpath=//select[@id='gender']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[3]/div/label/select", "xpath:idRelative"], + ["xpath=//select", "xpath:position"] + ], + "value": "" + }, { + "id": "f9341c3c-215b-44c1-9fff-fa977a7ba599", + "comment": "", + "command": "select", + "target": "id=gender", + "targets": [], + "value": "label=Male" + }, { + "id": "39458fd5-e6ac-46c7-9e48-b24349314ef0", + "comment": "", + "command": "click", + "target": "id=red", + "targets": [ + ["id=red", "id"], + ["name=colour", "name"], + ["css=#red", "css:finder"], + ["xpath=//input[@id='red']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[4]/div/input", "xpath:idRelative"], + ["xpath=//div/input", "xpath:position"] + ], + "value": "" + }, { + "id": "a1ded097-b395-4bed-b641-7c2ab136d411", + "comment": "", + "command": "click", + "target": "css=textarea", + "targets": [ + ["css=textarea", "css:finder"], + ["xpath=//form[@id='contactus']/div[5]/div/label/textarea", "xpath:idRelative"], + ["xpath=//textarea", "xpath:position"] + ], + "value": "" + }, { + "id": "582f0b4e-2521-404e-a912-39261d867d11", + "comment": "", + "command": "type", + "target": "css=textarea", + "targets": [ + ["css=textarea", "css:finder"], + ["xpath=//form[@id='contactus']/div[5]/div/label/textarea", "xpath:idRelative"], + ["xpath=//textarea", "xpath:position"] + ], + "value": "text" + }, { + "id": "764ae979-42dd-457e-95c5-5dddad91e44d", + "comment": "", + "command": "addSelection", + "target": "id=continent", + "targets": [ + ["id=continent", "id"], + ["css=#continent", "css:finder"], + ["xpath=//select[@id='continent']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[6]/div/label/select", "xpath:idRelative"], + ["xpath=//div[6]/div/label/select", "xpath:position"] + ], + "value": "label=North America" + }, { + "id": "bb929f6f-ee4c-43ad-920b-0806aba3c37d", + "comment": "", + "command": "click", + "target": "id=checkbox1", + "targets": [ + ["id=checkbox1", "id"], + ["css=#checkbox1", "css:finder"], + ["xpath=//input[@id='checkbox1']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[4]/div[2]/input", "xpath:idRelative"], + ["xpath=//div[2]/input", "xpath:position"] + ], + "value": "" + }, { + "id": "542d6958-faab-40e9-9b4b-81f051673c23", + "comment": "", + "command": "click", + "target": "id=submitbutton", + "targets": [ + ["id=submitbutton", "id"], + ["css=#submitbutton", "css:finder"], + ["xpath=//button[@id='submitbutton']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div[7]/div/button", "xpath:idRelative"], + ["xpath=//div/button", "xpath:position"], + ["xpath=//button[contains(.,'I do nothing!')]", "xpath:innerText"] + ], + "value": "" + }] + }, { + "id": "c0d8e914-0e45-4d59-be13-b902e026cb6c", + "name": "Test2", + "commands": [{ + "id": "319fa3e0-f697-4963-a879-b358fff62a77", + "comment": "", + "command": "open", + "target": "/selenium/testpage/", + "targets": [], + "value": "" + }, { + "id": "27aa1c23-21c7-45dc-8887-fe3d0e8f592b", + "comment": "", + "command": "setWindowSize", + "target": "1936x1056", + "targets": [], + "value": "" + }, { + "id": "421ace3d-1377-4423-9257-bfc703103beb", + "comment": "", + "command": "sendKeys", + "target": "id=firstname", + "targets": [], + "value": "test3" + }, { + "id": "301e2ba3-8f11-42cd-a41e-8241e158e962", + "comment": "", + "command": "sendKeys", + "target": "id=firstname", + "targets": [ + ["id=firstname", "id"], + ["css=#firstname", "css:finder"], + ["xpath=//input[@id='firstname']", "xpath:attributes"], + ["xpath=//form[@id='contactus']/div/div/label/input", "xpath:idRelative"], + ["xpath=//input", "xpath:position"] + ], + "value": "${KEY_ENTER}" + }] + }, { + "id": "8b6ff854-fefb-40bc-bd48-d206878b9e64", + "name": "Test3", + "commands": [{ + "id": "a3b6e82e-a847-4ac5-ac72-a05c5150d107", + "comment": "", + "command": "open", + "target": "http://www.tagindex.net/html/frame/example_f01.html", + "targets": [], + "value": "" + }, { + "id": "785d2ca1-c20b-4b96-a03e-43119ad2ecef", + "comment": "", + "command": "setWindowSize", + "target": "1936x1056", + "targets": [], + "value": "" + }, { + "id": "150bc283-671d-4d77-b1a5-b5dd20c2eee3", + "comment": "", + "command": "selectFrame", + "target": "index=0", + "targets": [ + ["index=0"] + ], + "value": "" + }, { + "id": "50809502-afdf-4b1b-943b-40524bbd2420", + "comment": "", + "command": "click", + "target": "css=html", + "targets": [ + ["css=html", "css:finder"], + ["xpath=//html", "xpath:position"] + ], + "value": "" + }, { + "id": "e726cc31-c25f-4cf7-92fe-77d976bb2f91", + "comment": "", + "command": "selectFrame", + "target": "relative=parent", + "targets": [ + ["relative=parent"] + ], + "value": "" + }, { + "id": "71ab81f7-8c7d-413e-a7ed-f68c224b1373", + "comment": "", + "command": "selectFrame", + "target": "index=1", + "targets": [ + ["index=1"] + ], + "value": "" + }, { + "id": "6e01154b-2221-4eef-98db-5760b52063e0", + "comment": "", + "command": "doubleClick", + "target": "css=p", + "targets": [ + ["css=p", "css:finder"], + ["xpath=//div[@id='MainBox']/p", "xpath:idRelative"], + ["xpath=//p", "xpath:position"], + ["xpath=//p[contains(.,'Please return to the previous page using the browsers \"back\" button.')]", "xpath:innerText"] + ], + "value": "" + }, { + "id": "a9f64bfd-1962-4f32-aa85-85d1ae4af91a", + "comment": "", + "command": "close", + "target": "", + "targets": [], + "value": "" + }] + }], + "suites": [{ + "id": "3fb340b3-1af6-4948-ad2a-c689126d9dad", + "name": "Default Suite", + "persistSession": false, + "parallel": false, + "timeout": 300, + "tests": ["0896d7a2-81f1-4972-bbc3-4880718bc157"] + }], + "urls": ["https://automationintesting.com/"], + "plugins": [] +} \ No newline at end of file From c111ef59cace2ae31dfb4f0763bbbfbbbfb86141 Mon Sep 17 00:00:00 2001 From: ili101 Date: Mon, 12 Oct 2020 21:18:59 +0300 Subject: [PATCH 2/5] Convert output to ScriptBlock --- ConvertTo-Selenium.ps1 | 70 ++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/ConvertTo-Selenium.ps1 b/ConvertTo-Selenium.ps1 index 77ad550..a4e4e57 100644 --- a/ConvertTo-Selenium.ps1 +++ b/ConvertTo-Selenium.ps1 @@ -1,5 +1,3 @@ -Import-Module .\Selenium.psd1 - function ConvertTo-Selenium { <# .SYNOPSIS @@ -71,44 +69,42 @@ function ConvertTo-Selenium { $Recording = Get-Content -Path $Path | ConvertFrom-Json $BaseUrl = [Uri]$Recording.url - '# Project: ' + $Recording.name - - foreach ($Test in $Recording.tests) { - '# Test: ' + $Test.name - foreach ($Command in $Test.commands) { - $PsCode = switch ($Command) { - { $_.comment } { '# Description: ' + $_.comment } - { $_.command -eq 'open' } { - $Url = if ([Uri]::IsWellFormedUriString($_.target, [System.UriKind]::Relative)) { - [Uri]::new($BaseUrl, $_.target) - } - else { - $_.target + $PsCode = $( + '# Project: ' + $Recording.name + foreach ($Test in $Recording.tests) { + '# Test: ' + $Test.name + foreach ($Command in $Test.commands) { + switch ($Command) { + { $_.comment } { '# Description: ' + $_.comment } + { $_.command -eq 'open' } { + $Url = if ([Uri]::IsWellFormedUriString($_.target, [System.UriKind]::Relative)) { + [Uri]::new($BaseUrl, $_.target) + } + else { + $_.target + } + { Set-SeUrl -Url $Url } | Get-Replace -From '$Url' -To $Url -QuotesTo + Break } - { Set-SeUrl -Url $Url } | Get-Replace -From '$Url' -To $Url -QuotesTo - Break - } - { $_.command -eq 'close' } { { Stop-SeDriver } ; Break } - { $_.command -in $ActionMap.Keys } { - $Action = $ActionMap[$_.command] | Get-Replace -From '$Keys' -To $_.value -QuotesTo -By $_.value - { Get-SeElement -By $By | _Action_ } | Get-Replace -From '_Action_' -To $Action -By $_.target - Break - } - { $_.command -eq 'selectFrame' } { - if ($_.target -eq 'relative=parent') { - { Switch-SeFrame -Parent } + { $_.command -eq 'close' } { { Stop-SeDriver } ; Break } + { $_.command -in $ActionMap.Keys } { + $Action = $ActionMap[$_.command] | Get-Replace -From '$Keys' -To $_.value -QuotesTo -By $_.value + { Get-SeElement -By $By | _Action_ } | Get-Replace -From '_Action_' -To $Action -By $_.target + Break } - else { - { $null = (Get-SeDriver -Current).SwitchTo().Frame($Index) } | Get-Replace -From '$Index' -To $_.target -SplitTo + { $_.command -eq 'selectFrame' } { + if ($_.target -eq 'relative=parent') { + { Switch-SeFrame -Parent } + } + else { + { $null = (Get-SeDriver -Current).SwitchTo().Frame($Index) } | Get-Replace -From '$Index' -To $_.target -SplitTo + } + Break } - Break + Default { '# Unsupported command. Command: "{0}", Target: "{1}", Value: "{2}", Comment: "{3}".' -f $_.command, $_.target, $_.value, $_.comment } } - Default { '# Unsupported command. Command: "{0}", Target: "{1}", Value: "{2}", Comment: "{3}".' -f $_.command, $_.target, $_.value, $_.comment } } - $PsCode | Get-Replace } - } -} -$PsCode = ConvertTo-Selenium -Path .\Example.side -$null = Start-SeDriver -Browser Chrome -. ([scriptblock]::Create(($PsCode | Out-String))) \ No newline at end of file + ) | Get-Replace + [ScriptBlock]::Create($PsCode -join [Environment]::NewLine) +} \ No newline at end of file From 4154466421ec5974b7bcb8b971a352e18d6c7278 Mon Sep 17 00:00:00 2001 From: ili101 Date: Mon, 12 Oct 2020 21:24:23 +0300 Subject: [PATCH 3/5] Move files --- Example.side => Examples/Example.side | 0 ConvertTo-Selenium.ps1 => Public/ConvertTo-Selenium.ps1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Example.side => Examples/Example.side (100%) rename ConvertTo-Selenium.ps1 => Public/ConvertTo-Selenium.ps1 (100%) diff --git a/Example.side b/Examples/Example.side similarity index 100% rename from Example.side rename to Examples/Example.side diff --git a/ConvertTo-Selenium.ps1 b/Public/ConvertTo-Selenium.ps1 similarity index 100% rename from ConvertTo-Selenium.ps1 rename to Public/ConvertTo-Selenium.ps1 From 02ba9bd86d4858a26111361f06de6a1f18dd2106 Mon Sep 17 00:00:00 2001 From: ili101 Date: Mon, 12 Oct 2020 22:21:42 +0300 Subject: [PATCH 4/5] Added "Invoke-SeClick -Action DoubleClick" to ConvertTo-Selenium --- Public/ConvertTo-Selenium.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Public/ConvertTo-Selenium.ps1 b/Public/ConvertTo-Selenium.ps1 index a4e4e57..5705980 100644 --- a/Public/ConvertTo-Selenium.ps1 +++ b/Public/ConvertTo-Selenium.ps1 @@ -61,10 +61,11 @@ function ConvertTo-Selenium { } $ActionMap = @{ - click = { Invoke-SeClick } - sendKeys = { Invoke-SeKeys -Keys $Keys } - type = { Invoke-SeKeys -Keys $Keys } - select = { Set-SeSelectValue -By $By } + click = { Invoke-SeClick } + doubleClick = { Invoke-SeClick -Action DoubleClick } + sendKeys = { Invoke-SeKeys -Keys $Keys } + type = { Invoke-SeKeys -Keys $Keys } + select = { Set-SeSelectValue -By $By } } $Recording = Get-Content -Path $Path | ConvertFrom-Json From beda411a34af8de042db29d6be7e13ce36622512 Mon Sep 17 00:00:00 2001 From: ili101 Date: Mon, 12 Oct 2020 22:25:01 +0300 Subject: [PATCH 5/5] Added "Switch-SeFrame -Frame $Index" to ConvertTo-Selenium Fix "Switch-SeFrame -Frame 0" --- Public/ConvertTo-Selenium.ps1 | 2 +- Public/Switch-SeFrame.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Public/ConvertTo-Selenium.ps1 b/Public/ConvertTo-Selenium.ps1 index 5705980..95d49bd 100644 --- a/Public/ConvertTo-Selenium.ps1 +++ b/Public/ConvertTo-Selenium.ps1 @@ -98,7 +98,7 @@ function ConvertTo-Selenium { { Switch-SeFrame -Parent } } else { - { $null = (Get-SeDriver -Current).SwitchTo().Frame($Index) } | Get-Replace -From '$Index' -To $_.target -SplitTo + { Switch-SeFrame -Frame $Index } | Get-Replace -From '$Index' -To $_.target -SplitTo } Break } diff --git a/Public/Switch-SeFrame.ps1 b/Public/Switch-SeFrame.ps1 index 368ea71..83c3e09 100644 --- a/Public/Switch-SeFrame.ps1 +++ b/Public/Switch-SeFrame.ps1 @@ -15,7 +15,7 @@ function Switch-SeFrame { Init-SeDriver -Driver ([ref]$Driver) -ErrorAction Stop #TODO Frame validation... Do not try to switch if element does not exist ? #TODO Review ... Maybe Parent / Root should be a unique parameter : -Level Parent/Root ) - if ($frame) { [void]$Driver.SwitchTo().Frame($Frame) } + if ($PSBoundParameters.ContainsKey('Frame')) { [void]$Driver.SwitchTo().Frame($Frame) } elseif ($Parent) { [void]$Driver.SwitchTo().ParentFrame() } elseif ($Root) { [void]$Driver.SwitchTo().defaultContent() } } \ No newline at end of file