Quamotion.PowerShell.psm1

$prefix = "http://localhost:17894/wd/hub"
$headers = @{"Content-Type" = "application/json" }
$findElementDelayMilliseconds = 100
$findElementMaxAttempts = 50
$currentSessionId = $null

<#
.SYNOPSIS
Connects to a remote WebDriver instance.
 
.DESCRIPTION
Connects to a remote WebDriver instance. The commands are forwared to the given remote url.
 
.PARAMETER at
The url of the remote WebDriver instance.
 
.EXAMPLE
ConnectTo-Quamotion -at http://qmwdjpn.quamotion.mobi/wd/hub
#>

function ConnectTo-Quamotion
{
    param ([string] $at)

    $script:prefix = $at
}

function Get-WebDriverStatus
{
    (Invoke-WebDriverRestMethod "$prefix/status" -Method Get).value
}

function New-DeveloperProfile
{
    param ([string] $accountName, [string] $accountPassword, [string] $password, [string] $outFile)

    $createDeveloperProfileRequest = @{
        AccountName = $accountName;
        AccountPassword = $accountPassword;
        Password = $password;
    }

    Invoke-WebDriverRestMethod "$prefix/quamotion/developercenter/profile" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $createDeveloperProfileRequest))) -Headers $headers -Method Put -OutFile $outFile
}

<#
.SYNOPSIS
Imports a folder containing the developer disks into the Quamotion WebDriver.
 
.PARAMETER developerDiskImageDirectory
A directory which, for each version of iOS you plan to support, contains a folder containing the developer
disk image for that version and the signature for that developer disk image.
 
.EXAMPLE
Import-DeveloperDisks /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS
#>

function Import-DeveloperDisks
{
    param (
        [Parameter(Mandatory=$true, Position = 1)] [string] $developerDiskImageDirectory)

    $directories = Get-ChildItem $developerDiskImageDirectory -Directory

    Foreach ($directory in $directories)
    {
        $developerDiskImage = Join-Path $directory.FullName "DeveloperDiskImage.dmg"
        $developerDiskSignature = Join-Path $directory.FullName "DeveloperDiskImage.dmg.signature"

        if ((Test-Path $developerDiskImage) -and (Test-Path $developerDiskSignature))
        {
            Write-Information "Uploading developer disk images in folder $directory)"
            Add-DeveloperDisk -developerDiskImage $developerDiskImage -developerDiskSignature $developerDiskSignature
        }
        else
        {
            Write-Warning "The folder $directory did not contain a DeveloperDiskImage.dmg and DeveloperDiskImage.dmg.signature file, skipping"
        }
    }
}

<#
.SYNOPSIS
Adds a developer disk to the Quamotion WebDriver.
 
.PARAMETER developerDiskImage
The developer disk image. Developer disk images are usually named DeveloperDiskImage.dmg
 
.PARAMETER developerDiskSignature
The signature file for the developer disk image. Signature files are usually named DeveloperDiskImage.dmg.signature
 
.EXAMPLE
Add-DeveloperDisk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS/11.0/DeveloperDiskImage.dmg /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS/11.0/DeveloperDiskImage.dmg.signature
#>

function Add-DeveloperDisk
{
    param (
        [Parameter(Mandatory=$true, Position = 1)] [string] $developerDiskImage,
        [Parameter(Mandatory=$true, Position = 2)] [string] $developerDiskSignature
    )

    if (-not (Test-Path $developerDiskImage) -or -not (Test-Path $developerDiskSignature))
    {
        Write-Error "Could not upload the developer disk because the file $developerDiskImage or $developerDiskSignature does not exist."
        return
    }

    try
    {
        Add-Type -AssemblyName System.Net.Http
        $client = New-Object System.Net.Http.HttpClient
        $content = New-Object System.Net.Http.MultipartFormDataContent

        $diskName = (Get-Item $developerDiskImage).Name
        $diskSignatureName = (Get-Item $developerDiskSignature).Name

        $diskStream = [System.IO.File]::OpenRead((Get-Item($developerDiskImage)).FullName)
        $diskSignature = [System.IO.File]::ReadAllBytes((Get-Item($developerDiskSignature)).FullName)

        $diskStreamContent = New-Object System.Net.Http.StreamContent $diskStream
        $diskSignatureContent = New-Object System.Net.Http.ByteArrayContent -ArgumentList @(,$diskSignature)

        $c = $content.Add($diskStreamContent, $diskName, $diskName)
        $c = $content.Add($diskSignatureContent, $diskSignatureName, $diskSignatureName)

        $result = $client.PostAsync("$prefix/quamotion/ios/developerDisk", $content).Result
        $c = $result.EnsureSuccessStatusCode()
    }
    catch
    {
        Write-Error "An error occurred while uploading $developerDiskImage and signature $developerDiskSignature"
    }
    finally
    {
        if ($diskStream) { $diskStream.Dispose() }
        if ($diskSignatureStream) { $diskSignatureStream.Dispose() }
    }
}

<#
.SYNOPSIS
Adds a developer profile to the Quamotion WebDriver
 
.PARAMETER developerProfile
The developer profile you want to add.
 
.PARAMETER developerDiskSignature
The password for the developer profile you want to add
 
.EXAMPLE
Add-DeveloperProfile company.developerprofile securepassword
#>

function Add-DeveloperProfile
{
    param (
        [Parameter(Mandatory=$true, Position = 1)] [string] $developerProfile,
        [Parameter(Mandatory=$true, Position = 2)] [string] $password
    )

    if (-not (Test-Path $developerProfile))
    {
        Write-Error "Could not upload the developer profile because the file $developerProfile does not exist."
        return
    }

    try
    {
        Add-Type -AssemblyName System.Net.Http
        $client = New-Object System.Net.Http.HttpClient
        $content = New-Object System.Net.Http.MultipartFormDataContent

        $passwordContent = New-Object System.Net.Http.StringContent $password

        $profileStream = [System.IO.File]::OpenRead($developerProfile)
        $profileStreamContent = New-Object System.Net.Http.StreamContent $profileStream

        $c = $content.Add($passwordContent, "password")
        $c = $content.Add($profileStreamContent, "profile", "profile")

        $result = $client.PostAsync("$prefix/quamotion/ios/developerProfile", $content).Result
        $c = $result.EnsureSuccessStatusCode()
    }
    catch
    {
        Write-Error "An error occurred while uploading $developerProfile"
    }
    finally
    {
        $profileStream.Dispose()
    }
}

<#
.SYNOPSIS
Uploads a license for the Quamotion WebDriver.
 
.PARAMETER license
The license file.
 
.EXAMPLE
Add-License quamotion.license
#>

function Add-License
{
    param (
        [Parameter(Mandatory=$true, Position = 1)] [string] $license
    )

    if (-not (Test-Path $license) )
    {
        Write-Error "Could not upload the license because the file $license does not exist."
        return
    }

    try
    {
        Add-Type -AssemblyName System.Net.Http
        $client = New-Object System.Net.Http.HttpClient
        $content = New-Object System.Net.Http.MultipartFormDataContent

        $licenseStream = [System.IO.File]::OpenRead((Get-Item $license).FullName)
        $licenseContent = New-Object System.Net.Http.StreamContent $licenseStream

        $c = $content.Add($licenseContent, "files", "quamotion.license")

        $result = $client.PostAsync("$prefix/quamotion/license", $content).Result
        $c = $result.EnsureSuccessStatusCode()
    }
    catch
    {
        Throw
        # Write-Error "An error occurred while uploading $license"
    }
    finally
    {
        if ($licenseStream) { $licenseStream.Dispose() }
    }
}

<#
.SYNOPSIS
List sessions on a device based on the application type and status.
 
.DESCRIPTION
List sessions on a device based on the application type and status.
 
.PARAMETER SessionId
The device id.
(Optional) the application type (Native, Web, Device, All).
(Optional) The session status (Unknown, Creating, Deploying, DeployFailed, Running, Stopping, StopFailed, All).
.EXAMPLE
Get-DeviceSessions -deviceId FUH7N16709007219
#>

Function Get-DeviceSessions
{
    param (
        [string] $deviceId,
        [string] $applicationType = 'All',
        [string] $status = 'All')

    return Get-Sessions | Where-Object {$_.Device.uniqueId -eq $deviceid -and ($_.Status -eq $status -or $status -eq 'All') -and ($_.Capabilities.applicationType -eq $applicationType -or $applicationType -eq 'All')}
}

<#
.SYNOPSIS
Retrieve the current window handle.
 
.DESCRIPTION
Retrieve the current window handle.
Returns: The current window handle.
NoSuchWindow - If the currently selected window has been closed.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-WindowHandle
#>

function Get-WindowHandle
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId
    return (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/window_handle").value
}

<#
.SYNOPSIS
Retrieve the list of all window handles available to the session.
 
.DESCRIPTION
Retrieve the list of all window handles available to the session.
Returns: A list of window handles.
The order in which the window handles are returned is arbitrary.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-WindowHandles
#>

function Get-WindowHandles
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId
    return (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/window_handles").value
}

<#
.SYNOPSIS
Gets the size of the current window.
 
.DESCRIPTION
Gets the size of the current window.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-WindowSize
#>

function Get-WindowSize
{
    param([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId
    return (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/window/size").value
}

<#
.SYNOPSIS
Change focus to another window.
 
.DESCRIPTION
Change focus to another window.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER windowName
The window to change focus to.
 
.EXAMPLE
Set-Window "Native"
#>

function Set-Window
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $windowName)
    
    Test-SessionId $sessionId

    $setWindowRequest = @{
        Name = $windowName;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/window" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $setWindowRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Close the current window.
 
.DESCRIPTION
Close the current window.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Close-Window
#>

function Close-Window
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/window" -Method Delete
}

<#
.SYNOPSIS
Get the current page source.
 
.DESCRIPTION
Get the current page source.
Returns: the current page source.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-Source
#>

function Get-Source
{
    param ([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    return (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/source").value
}

<#
.SYNOPSIS
Navigate to a new URL.
 
.DESCRIPTION
Navigate to a new URL.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER url
The URL to navigate to.
 
.EXAMPLE
Navigate-To -url "http:\\quamotion.mobi"
#>

function Navigate-To
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $url)
    
    Test-SessionId $sessionId

    $elementRequest = @{
        url = $url;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/url" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Reloads the current webpage.
 
.DESCRIPTION
Reloads the current webpage optionally ignoring the cache.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER ignoreCache
If true, browser cache is ignored (as if the user pressed Shift+refresh).
 
.EXAMPLE
Reload-Page
#>

function Reload-Page
{
    param ([string] $sessionId = $script:currentSessionId,
           [bool] $ignoreCache = $true)
    
    Test-SessionId $sessionId

    $reloadRequest = @{
        ignoreCache = $ignoreCache;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/refresh" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $reloadRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Retrieve the URL of the current page.
 
.DESCRIPTION
Retrieve the URL of the current page.
Returns: the current url.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-Url
#>

function Get-Url
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $url = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/url" -Headers $headers -Method Get).value

    return $url
}

<#
.SYNOPSIS
Search for the top element in the current window on position (x, y).
 
.DESCRIPTION
Search for the top element in the current window on position (x, y).
The coordinates are expressed in pixels and measured from the top left corner.
Returns: the element on position (x, y)
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER x
The x coordinate
 
.PARAMETER y
the y coordinate
 
.EXAMPLE
Find-ElementByCoordinate -x 200 -y 500
#>

function Find-ElementByCoordinate
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [int] $x,
        [Parameter(Mandatory=$true, Position = 2)] [int] $y)
    
    Test-SessionId $sessionId

    $elementRequest = @{
        x = $x;
        y = $y;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/elementByCoordinates" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Search for a element whose identifier matches the given value.
 
.DESCRIPTION
Search for element whose identifier attribute matches the given value.
Returns: the first element whose identifier attribute matches the given value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER id
The indentifier of the element to search.
 
.EXAMPLE
Find-ElementById -id 123456
#>

function Find-ElementById
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $id)
    
    Test-SessionId $sessionId

    $elementRequest = @{
        using = "id";
        value = $id;
    }

    $element = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/elements" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post).value

    if($element.Length -gt 1)
    {
        Write-Warning "More than one element has element id: $id"
    }

    Write-Verbose "Element $($id): $element[0].ELEMENT"

    return $element[0].ELEMENT
}

<#
.SYNOPSIS
Search for all elements whose identifier matches the given value.
 
.DESCRIPTION
Search for all elements whose identifier attribute matches the given value.
Returns: all elements whose identifier attribute matches the given value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER id
The indentifier of the element to search.
 
.EXAMPLE
Find-ElementsById -id 123456
#>

function Find-ElementsById
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $id)
    
    Test-SessionId $sessionId

    $elementRequest = @{
        using = "id";
        value = $id;
    }

    $elements = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/elements" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post).value

    return $elements
}

<#
.SYNOPSIS
Go back, if possible.
 
.DESCRIPTION
Go back, if possible.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Go-Back
#>

function Go-Back
{
    param ([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    Write-Verbose "Clicking back button"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/back" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Go to the home screen
 
.DESCRIPTION
Go to the home screen (only for Full Device Automation sessions)
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Go-ToHomeScreen
#>

function Go-ToHomeScreen
{
    param ([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/wda/homescreen" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Tests whether elements exist matching the given xpath.
 
.DESCRIPTION
Tests whether elements exist matching the given xpath. Test-Elements does not take the implicit timeout into account.
Returns: $true or $false depending on the existens of the element.
Throws exception when the element is not found because there is a popup present.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath for which to test the element.
 
.PARAMETER ignoreVisibility
(Optional) indicates whether non visible elements should be taken into account.
 
.EXAMPLE
Test-Elements -xpath "*[@marked='login']"
#>

function Test-Elements
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ParameterSetname = "xpath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$false)] [bool] $ignoreVisibility = $false)
    
    Test-SessionId $sessionId
    
    if($xpath)
    {
        $elementRequest = @{
            using = "xpath";
            value = $xpath;
            ignoreVisibility = $ignoreVisibility;
        }
    }
    else
    {
        $elementRequest = @{
            using = $using;
            value = $value;
            ignoreVisibility = $ignoreVisibility;
        }
    }

    try
    {
        $response = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/elements" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post)
        $element = $response.value

        return $element.Length -gt 0
    }
    catch
    {
        # check if there is an alert obstructing the call
        If(($_.Exception.GetType() -eq [WebDriverException]) -and $_.Exception.Status -eq 7)
        {
            return $false
        }
        Else
        {
            throw $_
        }
    }
}

<#
.SYNOPSIS
Tests whether an element exist matching the given xpath
 
.DESCRIPTION
Tests whether an element exist matching the given xpath
Returns: $true or $false depending on the existens of the element.
 
Throws exception when the element is not found because there is a popup present.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath for which to test the element.
 
.PARAMETER ignoreVisibility
(Optional) indicates whether non visible elements should be taken into account.
 
.EXAMPLE
Test-Element -xpath "*[@marked='login']"
#>

function Test-Element
{
   param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ParameterSetname = "xpath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$false)] [bool] $ignoreVisibility = $false)
    
    Test-SessionId $sessionId

    if($xpath)
    {
        $elementRequest = @{
            using = "xpath";
            value = $xpath;
            ignoreVisibility = $ignoreVisibility;
        }
    }
    else
    {
        $elementRequest = @{
            using = $using;
            value = $value;
            ignoreVisibility = $ignoreVisibility;
        }
    }

   try
   {
       $response = (Invoke-WebDriverRestMethod “$prefix/session/$sessionId/element” -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post)
       $element = $response.value

       return $element -ne $null
   }
    catch
    {
        # Did we find elements?
       If(($_.Exception.GetType() -eq [WebDriverException]) -and $_.Exception.Status -eq 7)
       {
           return $false
       }
       Else
       {
           throw $_
       }
    }
}

<#
.SYNOPSIS
Search for all elements whose xpath matches the given value.
 
.DESCRIPTION
Search for all elements whose xpath attribute matches the given value.
Returns: all elements whose xpath attribute matches the given value.
Throws exception when there is a popup present.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath used to locate elements.
 
.PARAMETER ignoreVisibility
(Optional) indicates whether non visible elements should be taken into account.
 
.PARAMETER parentElement
(Optional) limits the search to only descendants of this element.
 
.EXAMPLE
Find-Elements -xpath "*[@marked='login']"
#>

function Find-Elements
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ParameterSetname = "xpath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$false)] [bool] $ignoreVisibility = $false,
        [Parameter(Mandatory=$false)] [string] $parentElement = $null)
    
    Test-SessionId $sessionId

    if($xpath)
    {
        $elementRequest = @{
            using = "xpath";
            value = $xpath;
            ignoreVisibility = $ignoreVisibility;
        }
    }
    else
    {
        $elementRequest = @{
            using = $using;
            value = $value;
            ignoreVisibility = $ignoreVisibility;
        }
    }

    Try
    {
        if ($parentElement)
        {
             $path = "$prefix/session/$sessionId/element/$parentElement/elements"
        }
        else
        {
            $path = "$prefix/session/$sessionId/elements"
        }
    }
    catch
    {
        If(($_.Exception.GetType() -eq [WebDriverException]) -and $_.Exception.Status -eq 7)
        {
            return @()
        }
        Else
        {
            throw $_
        }
    }

    Try
    {
        $elements = (Invoke-WebDriverRestMethod $path -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post).value
        return $elements
    }
    catch
    {
        # check if there is an alert obstructing the call
        If(($_.Exception.GetType() -eq [WebDriverException]) -and $_.Exception.Status -eq 7)
        {
            return @()
        }
        Else
        {
            throw $_
        }
    }
}

<#
.SYNOPSIS
Search for an element whose xpath matches the given value.
 
.DESCRIPTION
Search for an element whose xpath attribute matches the given value.
Returns: the first element whose xpath attribute matches the given value.
This method will wait for the element to appear on your application during
the amount of time specified by Set-Timeout.
 
Throws an exception there is no element found matching the given xpath.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath used to locate elements.
 
.PARAMETER ignoreVisibility
(Optional) indicates whether non visible elements should be taken into account.
 
.PARAMETER parentElement
(Optional) limits the search to only descendants of this element.
 
.EXAMPLE
Find-Element -xpath "*[@marked='login']"
#>

function Find-Element
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ParameterSetName = "xpath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$false)] [bool] $ignoreVisibility = $false,
        [Parameter(Mandatory=$false)] [string] $parentElement = $null)
    
    Test-SessionId $sessionId

    if($xpath)
    {
        $elementRequest = @{
        using = "xpath";
            value = $xpath;
            ignoreVisibility = $ignoreVisibility;
        }
    }
    else
    {
        $elementRequest = @{
            using = $using;
            value = $value;
            ignoreVisibility = $ignoreVisibility;
        }
    }

    if ($parentElement)
    {
        $path = "$prefix/session/$sessionId/element/$parentElement/element"
    }
    else
    {
        $path = "$prefix/session/$sessionId/element"
    }

    $response = (Invoke-WebDriverRestMethod $path -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $elementRequest))) -Headers $headers -Method Post).value

    $element = $response.ELEMENT

    Write-Verbose "Element $xpath has ID $element"

    return $element
}

<#
.SYNOPSIS
Double clicks on the first element whose locator matches the given value.
 
.DESCRIPTION
Double clicks on the first element whose locator matches the given value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath used to locate the element.
 
.PARAMETER Marked
The marked attribute value used to locate the element.
 
.PARAMETER Class
The class attribute value used to locate the element.
 
.EXAMPLE
DoubleClick-Element -xpath "*[@marked='login']"
DoubleClick-Element -marked "login"
DoubleClick-Element -class "UIView"
#>

function DoubleClick-Element
{
    param (
        [Parameter(ParameterSetName = "ElementId")]
        [Parameter(ParameterSetName = "Marked")]
        [Parameter(ParameterSetName = "XPath")]
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, ParameterSetName = "ElementId", ValueFromPipeline=$True)]
        [string] $elementId,
        [Parameter(Mandatory=$true, ParameterSetName = "XPath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$true, ParameterSetName = "Marked")]
        [string] $marked)
    
    Test-SessionId $sessionId

    if ($elementId)
    {
         $id = $elementId
    }
    elseif ($xpath)
    {
        Write-Verbose "Clicking on element $xpath"
        $id = Find-Element -sessionId $sessionId -xpath $xpath
    }
    elseif ($marked)
    {
        Write-Verbose "Clicking on element marked $marked"
        $id = Find-Element -sessionId $sessionId -xpath "*[@marked='$marked']"
    }
    Else{
        Write-Verbose "Clicking on element with value $value using $using"
        $id = Find-Element -sessionId $sessionId -value $value -using $using
    }

    if ($id -And $id -ne $null -And $id -ne "")
    {
        $doubleClickRequest = @{
            element = $id
        }

        Write-Verbose "Double click on element $id"
        Invoke-WebDriverRestMethod "$prefix/session/$sessionId/touch/doubleclick" -Headers $headers -Body (ConvertTo-Json $doubleClickRequest) -Method Post
    }
    else
    {
        Report-Status -message "No matching element found." -succes $false
    }
}

<#
.SYNOPSIS
Clicks on the first element whose locator matches the given value.
 
.DESCRIPTION
Clicks on the first element whose locator matches the given value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
The xpath used to locate elements.
 
.PARAMETER Marked
The marked attribute value used to locate elements.
 
.PARAMETER Class
The class attribute value used to locate elements.
 
.EXAMPLE
Click-Element -xpath "*[@marked='login']"
Click-Element -marked "login"
Click-Element -class "UIView"
#>

function Click-Element
{
    param (
        [Parameter(ParameterSetName = "ElementId")]
        [Parameter(ParameterSetName = "Marked")]
        [Parameter(ParameterSetName = "XPath")]
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, ParameterSetName = "ElementId", ValueFromPipeline=$True)]
        [string] $elementId,
        [Parameter(Mandatory=$true, ParameterSetName = "XPath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [LocatorStrategy] $using,
        [Parameter(Mandatory=$true, ParameterSetName = "explicit")]
        [string] $value,
        [Parameter(Mandatory=$true, ParameterSetName = "Marked")]
        [string] $marked)
    
    Test-SessionId $sessionId

    if ($elementId)
    {
         Write-Verbose "Clicking on element $elementId"
         Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/click" -Headers $headers -Method Post
    }
    else
    {
        if ($xpath)
        {
            Write-Verbose "Clicking on element $xpath"
            $id = Find-Element -sessionId $sessionId -xpath $xpath
        }
        elseif ($marked)
        {
             Write-Verbose "Clicking on element marked $marked"
             $id = Find-Element -sessionId $sessionId -xpath "*[@marked='$marked']"
        }
        Else{
            Write-Verbose "Clicking on element with value $value using $using"
             $id = Find-Element -sessionId $sessionId -value $value -using $using
        }
        if ($id -And $id -ne $null -And $id -ne "")
        {
            Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$id/click" -Headers $headers -Method Post
        }
        else
        {
            Report-Status -message "No element found for $xpath" -succes $false
        }
    }
}

function Submit-Element
{
    param (
        [Parameter(ParameterSetName = "ElementId")]
        [Parameter(ParameterSetName = "Marked")]
        [Parameter(ParameterSetName = "XPath")]
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, ParameterSetName = "ElementId", ValueFromPipeline=$True)]
        [string] $elementId,
        [Parameter(Mandatory=$true, ParameterSetName = "XPath")]
        [string] $xpath)
    
    Test-SessionId $sessionId

    if ($elementId)
    {
         Write-Verbose "Clicking on element $elementId"
         Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/submit" -Headers $headers -Method Post
    }
    elseif ($xpath)
    {
         Write-Verbose "Clicking on element $xpath"
         $id = Find-Element -sessionId $sessionId -xpath $xpath
         Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$id/submit" -Headers $headers -Method Post
    }
}

<#
.SYNOPSIS
Gets the value of an element property.
 
.DESCRIPTION
Gets the value of an element property.
Returns: the value of the property
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to query.
 
.PARAMETER property
The property name to query.
 
.EXAMPLE
Get-ElementProperty 12345 "Text"
Find-Element "login" | Get-ElementProperty "Text"
#>

function Get-ElementProperty
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline)] [string] $elementId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $property)
    
    begin {
        Test-SessionId $sessionId
    }

    Process {
        $text = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/property/$property" -Headers $headers -Method Get).value
        $text
    }
}

<#
.SYNOPSIS
Gets all property names of an element.
 
.DESCRIPTION
Gets all property names of an element.
Returns: all property names of an element.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to query.
 
.EXAMPLE
Get-ElementProperties 12345
Find-Element "login" | Get-ElementProperties
#>

function Get-ElementProperties
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline)] [string] $elementId)

    Test-SessionId $sessionId

    $text = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/property" -Headers $headers -Method Get).value

    return $text
}

<#
.SYNOPSIS
Gets the text value of an element.
 
.DESCRIPTION
Gets the text value of an element.
Returns: the text value of an element.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to query.
 
.EXAMPLE
Find-Element "login" | Get-ElementText
#>

function Get-ElementText
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline)] [string] $elementId)
    
    Test-SessionId $sessionId


    $text = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/text" -Headers $headers -Method Get).value

    return $text
}

function Get-MapMarkers
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $markers = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/markers" -Headers $headers -Method Get).value

    return $markers
}

function Get-MapBounds
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $bounds = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/bounds" -Headers $headers -Method Get).value

    return $bounds
}

function Get-MapCenter
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $position = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/center" -Headers $headers -Method Get).value

    return $position
}

function Get-MapZoom
{
    param (
        [string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $zoom = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/zoom" -Headers $headers -Method Get).value

    return $zoom
}

function Set-MapZoom
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [int] $level)
    
    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/zoom" -Headers $headers -Body (ConvertTo-Json $level) -Method Post
}

function Set-MapCenter
{
    param (
    [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [double] $latitude,
        [Parameter(Mandatory=$true, Position = 2)] [double] $longitude)
    
    Test-SessionId $sessionId

    $position = @{
        latitude = $latitude;
        longitude = $longitude;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/map/center" -Headers $headers -Body (ConvertTo-Json $position) -Method Post

    return $zoom
}

<#
.SYNOPSIS
Gets the bounds of an element.
 
.DESCRIPTION
Gets the bounds of an element.
Returns: a rectangle reprenting the outer bounds of an element.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to query.
 
.EXAMPLE
Find-Element "login" | Get-ElementRectangle
#>

function Get-ElementRectangle
{
    param (
        [string] $sessionId = $script:currentSessionId, 
        [Parameter(Mandatory=$true, Position = 1)] [string] $elementId)
    
    Test-SessionId $sessionId

    $rectangle = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/rect" -Headers $headers -Method Get).value
    $displayed = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/displayed" -Headers $headers -Method Get).value

    return @{
        X = $rectangle.X;
        Y = $rectangle.Y;
        Width = $rectangle.Width;
        Height = $rectangle.Height;
        Displayed = $displayed;
    }
}


<#
.SYNOPSIS
Performs a long click on the first element whose locator matches the given value.
 
.DESCRIPTION
Performs a long click on the first element whose locator matches the given value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to click.
 
.EXAMPLE
Find-Element -marked "Login" | LongClick-Element
#>

function LongClick-Element
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $elementId)
    
    Test-SessionId $sessionId

    $flickRequest = @{
        element = $elementId
    }

    Write-Verbose "Long clicking on element $elementId"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/touch/longclick" -Headers $headers -Body (ConvertTo-Json $flickRequest) -Method Post
}

<#
.SYNOPSIS
Performs a flick based on coordinates.
 
.DESCRIPTION
Performs a flick based on coordinates.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element.
 
.PARAMETER x
x coordinate of the start point relative from the element
 
.PARAMETER y
y coordinate of the start point relative from the element
 
.PARAMETER xoffset
the horizontal distance to flick
 
.PARAMETER xoffset
the vertical distance to flick
 
.PARAMETER speed
the speed of the flick movement
 
.EXAMPLE
Flick-Coordinate -elementId 12244 -x 100 -y 100 -xoffset 0 -yoffset -300
#>

function Flick-Coordinate
{
  param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true)] [int] $x,
        [Parameter(Mandatory=$true)] [int] $y,
        [Parameter(Mandatory=$true)] [int] $xoffset,
        [Parameter(Mandatory=$true)] [int] $yoffset,
        [Parameter(Mandatory=$true)] [int] $speed)
    
    Test-SessionId $sessionId

    $flickRequest = @{
            xCoordinate = $x;
            yCoordinate = $y;
            yoffset = $yoffset;
            xoffset = $xoffset;
            speed = $speed;
    }

    Write-Verbose "Flicking on element $elementId"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/touch/flick" -Headers $headers -Body (ConvertTo-Json $flickRequest)  -Method Post
}

<#
.SYNOPSIS
Performs a flick on an element.
 
.DESCRIPTION
Performs a flick on an element identified by one of the available locators.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
(id locator) The identifier of the element.
 
.PARAMETER xpath
(xpath locator) The xpath of the element
 
.PARAMETER marked
(marked locator) The marked expression of the element
 
.PARAMETER xOffset
the horizontal distance to flick
 
.PARAMETER yOffset
the vertical distance to flick
 
.PARAMETER speed
the speed of the flick movement
 
.EXAMPLE
Flick-Element -marked "login" -xoffset 0 -yoffset -300
#>

function Flick-Element
{
    param (
        [Parameter(ParameterSetName = "ElementId")]
        [Parameter(ParameterSetName = "Marked")]
        [Parameter(ParameterSetName = "XPath")]
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, ParameterSetName = "ElementId", ValueFromPipeline=$True)]
        [string] $elementId,
        [Parameter(Mandatory=$true, ParameterSetName = "XPath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "Marked")]
        [string] $marked,
        [Parameter(Mandatory=$true)] [int] $xOffset,
        [Parameter(Mandatory=$true)] [int] $yOffset,
        [Parameter(Mandatory=$true)] [int] $speed,
        [ValidateSet("up", "down", "left", "right")]
        [string] $direction)
    
    Test-SessionId $sessionId

    if ($elementId)
    {
         Write-Verbose "Flick on element $elementId"
         $id = $elementId
    }
    elseif ($xpath)
    {
         Write-Verbose "Flick on element $xpath"
         $id = Find-Element -sessionId $sessionId -xpath $xpath
    }
    else
    {
         Write-Verbose "Flick on element marked $marked"
         $id = Find-Element -sessionId $sessionId -xpath "*[@marked='$marked']"
    }

    $flickRequest = @{
        element = $id;
        yoffset = $yOffset;
        xoffset = $xOffset;
        speed = $speed;
        direction = $direction;
    }

    Write-Verbose "Flicking on element $elementId"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/touch/flick" -Headers $headers -Body (ConvertTo-Json $flickRequest)  -Method Post
}

<#
.SYNOPSIS
Performs a click on a coordinate.
 
.DESCRIPTION
Performs a click on a coordinate. The coordinate is measured from the top left corner of the screen.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER x
x coordinate of the start point relative from the element
 
.PARAMETER y
y coordinate of the start point relative from the element
 
s.EXAMPLE
Click-Coordinate -x 100 -y 100
#>

function Click-Coordinate
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [int] $x,
        [Parameter(Mandatory=$true, Position = 2)] [int] $y)
    
    Test-SessionId $sessionId

    $clickRequest = @{
        y = $y;
        x = $x;
    }

    Write-Verbose "Clicking on coodinate ($x, $y)"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/touch/clickByCoordinate" -Headers $headers -Body (ConvertTo-Json $clickRequest)  -Method Post
}

<#
.SYNOPSIS
Send a sequence of key strokes to the active element.
 
.DESCRIPTION
Send a sequence of key strokes to the active element.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER text
The keys sequence to be sent.
 
.EXAMPLE
Enter-Text "Quamotion"
#>

function Enter-Text
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $text)
    
    Test-SessionId $sessionId

    $keysRequest = @{
        value = @($text);
    }

    Write-Verbose "Typing $text"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/keys" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $keysRequest)))  -Method Post
}

function Set-ElementProperty
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline)] [string] $elementId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $propertyName,
        [Parameter(Mandatory=$true, Position = 3)] [object] $propertyValue)
    
    Test-SessionId $sessionId

    $request = @{
        propertyName = $propertyName;
        propertyValue = $propertyValue;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/property" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $request)))  -Method Post
}

function Perform-Operation
{
param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline)] [string] $elementId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $operation,
        [Parameter(Mandatory=$true, Position = 3)] [object[]] $arguments)
    
    Test-SessionId $sessionId

    $request = @{
        operation = $operation;
        args = $arguments;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/perform" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $request)))  -Method Post
}

<#
.SYNOPSIS
Gets the current session identifier.
 
.DESCRIPTION
Gets the current session identifier.
Returns: the current session identifiers
 
.EXAMPLE
Get-CurrentSessionId
#>

function Get-CurrentSessionId
{
    return $script:currentSessionId
}

<#
.SYNOPSIS
Sets the current session identifier.
 
.DESCRIPTION
Sets the current session identifier.
 
.PARAMETER sessionId
The identifier of the session to be used.
 
.EXAMPLE
Set-CurrentSessionId -sessionId "e68ef189-cd1b-4def-a1e2-1b4e1f1526df"
#>

function Set-CurrentSessionId
{
    param (
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName)] [string] $sessionId)

    $script:currentSessionId = $sessionId
}

<#
.SYNOPSIS
Create a new session.
 
.DESCRIPTION
Create a new session. The session will install and launch the application on the device.
 
.PARAMETER deviceId
The identifier of the device to be used.
This identifier can be queried by perfoming the Get-Device commmand or by clicking on the device in the frontend.
 
.PARAMETER appId
The identifier of the application to be used.
This identifier can be queried by perfoming the Get-App commmand or by clicking on the application in the frontend.
 
.PARAMETER appVersion
The version of the application to be used.
The version can be queried by perfoming the Get-App commmand or by clicking on the application in the frontend.
 
.PARAMETER appVersion
The version of the application to be used.
The version can be queried by perfoming the Get-App commmand or by clicking on the application in the frontend.
 
.PARAMETER clearAppSettings
Indicates whether the application settions should be cleared before installing the application.
 
.PARAMETER reinstallApplication
Indicates whether the application should reinstalled.
 
.PARAMETER reuseExistingSession
Indicates whether an existing session should be reused.
A session is only reused when the appId, appVersion and deviceId are equal
 
.EXAMPLE
New-Session -appId demo.quamotion.Acquaint -appVersion "1.51" -deviceId $iPhone6 -reuseExistingSession $true
#>

function New-Session
{
    param (
        [Parameter(Mandatory=$false)] [string] $appVersion = $null,
        [Parameter(Mandatory=$false)] [bool] $clearAppSettings = $false,
        [Parameter(Mandatory=$false)] [bool] $reinstallApplication = $false,
        [Parameter(Mandatory=$false)] [bool] $reuseExistingSession = $false,
        [Parameter(Mandatory=$false)] [bool] $recordingPrestart = $false,
        [Parameter(Mandatory=$false)] [bool] $takesScreenshot = $true)

    DynamicParam {
        # Create the dictionary for all the parameters
        $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        ## Device Id

        # Set the dynamic parameters' name
        $DeviceIdParameterName = 'deviceId'

        # Create the collection of attributes
        $DeviceIdAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Create and set the parameters' attributes
        $DeviceIdParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $DeviceIdParameterAttribute.Mandatory = $true

        # Add the attributes to the attributes collection
        $DeviceIdAttributeCollection.Add($DeviceIdParameterAttribute)

        # Generate and set the ValidateSet
        $devices = Get-Device
        $DeviceIdArrSet = $devices | Select-Object -ExpandProperty uniqueId
        $DeviceIdValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($DeviceIdArrSet)

        # Add the ValidateSet to the attributes collection
        $DeviceIdAttributeCollection.Add($DeviceIdValidateSetAttribute)

        # Create and return the dynamic parameter
        $DeviceIdRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($DeviceIdParameterName, [string], $DeviceIdAttributeCollection)
        $RuntimeParameterDictionary.Add($DeviceIdParameterName, $DeviceIdRuntimeParameter)

        ## AppId

        # Set the dynamic parameters' name
        $AppIdParameterName = 'appId'

        # Create the collection of attributes
        $AppIdAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Create and set the parameters' attributes
        $AppIdParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $AppIdParameterAttribute.Mandatory = $true

        # Add the attributes to the attributes collection
        $AppIdAttributeCollection.Add($AppIdParameterAttribute)

        # Generate and set the ValidateSet
        $apps = Get-App
        $AppIdArrSet = $apps | Select-Object -ExpandProperty AppId
        $AppIdValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($AppIdArrSet)

        # Add the ValidateSet to the attributes collection
        $AppIdAttributeCollection.Add($AppIdValidateSetAttribute)

        # Create and return the dynamic parameter
        $AppIdRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($AppIdParameterName, [string], $AppIdAttributeCollection)
        $RuntimeParameterDictionary.Add($AppIdParameterName, $AppIdRuntimeParameter)

        return $RuntimeParameterDictionary
    }

    begin {
        # Bind the parameter to a friendly variable
        $deviceId = $PsBoundParameters[$DeviceIdParameterName]
        $appId = $PsBoundParameters[$AppIdParameterName]
    }

    process {
        if(!$appVersion)
        {
            $sessionRequest = @{
                RequiredCapabilities =
                @{
                    deviceId = $deviceId;
                    applicationType = "Native";
                    appId = $appId;
                    clearApplicationSettings = $clearAppSettings;
                    reinstallApplication = $reinstallApplication;
                    recordingPrestart = $recordingPrestart;
                    reuseExistingSession = $reuseExistingSession;
                    takesScreenshot = $takesScreenshot;
                }
            }
        }
        else
        {
            $sessionRequest = @{
                RequiredCapabilities =
                @{
                    deviceId = $deviceId;
                    applicationType = "Native";
                    appId = $appId;
                    appVersion = $appVersion;
                    clearApplicationSettings = $clearAppSettings;
                    reinstallApplication = $reinstallApplication;
                    recordingPrestart = $recordingPrestart;
                    reuseExistingSession = $reuseExistingSession;
                    takesScreenshot = $takesScreenshot;
                }
            }
        }

        $createSessionResponse = Invoke-WebDriverRestMethod "$prefix/session" -Headers $headers -Body (ConvertTo-Json $sessionRequest) -Method Post
        $sessionId = $createSessionResponse.sessionId

        # Wait for the session to start
        while (-not (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/isReady").value)
        {
            # Get detailed status information
            $status = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/status").value

            # Break this loop if Status is DeployFailed or be stuck forever
            if($status.Status -eq "DeployFailed")
            {
                Throw "Deploy session failed: $($status.StatusMessage)"
            }
            # Filter duplicate messages in the output, unless verbose flag is set
            if(($statusOld -ne $status.Status) -or ($statusMessageOld -ne $status.StatusMessage))
            {
                Write-Host "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
                $statusOld = $status.Status
                $statusMessageOld = $status.StatusMessage
            }
            else
            {
                Write-Verbose "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
            }
            Start-Sleep -Milliseconds 1000
        }

        if ($script:currentSessionId -ne $null)
        {
            Write-Warning "Overriding the currently set default session"
        }
        else
        {
            Write-Information "Using this session as your default session"
        }

        $script:currentSessionId = $sessionId

        return $sessionId
    }
}

<#
.SYNOPSIS
Removes a session
 
.DESCRIPTION
Removes a session.
The application is closed.
 
.PARAMETER sessionId
(Optional) The identifier of the session to be used.
 
.EXAMPLE
Remove-Session
#>

function Remove-Session
{
    param ([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId" -Method Delete

    $script:currentSessionId = $null
}

<#
.SYNOPSIS
Gets all active session
 
.DESCRIPTION
Gets all active sessions
Returns: all active sessions
 
.EXAMPLE
Get-Sessions
#>

function Get-Sessions
{
    return (Invoke-WebDriverRestMethod -Uri "$prefix/sessions").value
}

function Get-EndPoints
{
    param ([string] $sessionId = $script:currentSessionId)
    
    Test-SessionId $sessionId

    $endPoints = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/endPoints" -Headers $headers -Method Get).value

    return $endPoints
}

function New-WebSession
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [bool] $clearAppSettings = $false,
        [Parameter(Mandatory=$false)] [bool] $takesScreenshot = $true)

    $sessionRequest = @{
        RequiredCapabilities =
        @{
            deviceId = $deviceId;
            applicationType = "Web";
            clearApplicationSettings = $clearAppSettings;
            takesScreenshot = $takesScreenshot;
        }
    }

    $createSessionResponse = Invoke-WebDriverRestMethod "$prefix/session" -Headers $headers -Body (ConvertTo-Json $sessionRequest) -Method Post
    $sessionId = $createSessionResponse.sessionId

    # Wait for the session to start
    while (-not (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/isReady").value)
    {
        # Get detailed status information
        $status = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/status").value

        # Break this loop if Status is DeployFailed or be stuck forever
        if($status.Status -eq "DeployFailed")
        {
            Throw "Deploy session failed: $($status.StatusMessage)"
        }
        # Filter duplicate messages in the output, unless verbose flag is set
        if(($statusOld -ne $status.Status) -or ($statusMessageOld -ne $status.StatusMessage))
        {
            Write-Host "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
            $statusOld = $status.Status
            $statusMessageOld = $status.StatusMessage
        }
        else
        {
            Write-Verbose "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
        }
        Start-Sleep -Milliseconds 1000
    }

    if ($script:currentSessionId -ne $null)
    {
        Write-Warning "Overriding the currently set default session"
    }
    else
    {
        Write-Information "Using this session as your default session"
    }

    $script:currentSessionId = $sessionId

    return $sessionId
}

<#
.SYNOPSIS
Starts a session which allows you to automate the entire device.
 
.DESCRIPTION
Starts a session which allows you to automate the entire device.
 
.EXAMPLE
New-DeviceSession "device-udid-here"
#>

function New-DeviceSession
{
    param (
        [Parameter(Mandatory=$false)] [bool] $reuseExistingSession = $false,
        [Parameter(Mandatory=$false)] [bool] $takesScreenshot = $true
    )

    DynamicParam {
        # Create the dictionary for all the parameters
        $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        ## Device Id

        # Set the dynamic parameters' name
        $DeviceIdParameterName = 'deviceId'

        # Create the collection of attributes
        $DeviceIdAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Create and set the parameters' attributes
        $DeviceIdParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $DeviceIdParameterAttribute.Mandatory = $true

        # Add the attributes to the attributes collection
        $DeviceIdAttributeCollection.Add($DeviceIdParameterAttribute)

        # Generate and set the ValidateSet
        $devices = Get-Device
        $DeviceIdArrSet = $devices | Select-Object -ExpandProperty uniqueId
        $DeviceIdValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($DeviceIdArrSet)

        # Add the ValidateSet to the attributes collection
        $DeviceIdAttributeCollection.Add($DeviceIdValidateSetAttribute)

        # Create and return the dynamic parameter
        $DeviceIdRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($DeviceIdParameterName, [string], $DeviceIdAttributeCollection)
        $RuntimeParameterDictionary.Add($DeviceIdParameterName, $DeviceIdRuntimeParameter)
        return $RuntimeParameterDictionary
    }

    begin {
        # Bind the parameter to a friendly variable
        $deviceId = $PsBoundParameters[$DeviceIdParameterName]
    }

    process {
        $sessionRequest = @{
            RequiredCapabilities =
            @{
                deviceId = $deviceId;
                applicationType = "Device";
                reuseExistingSession = $reuseExistingSession;
                takesScreenshot = $takesScreenshot;
            }
        }

        $createSessionResponse = Invoke-WebDriverRestMethod "$prefix/session" -Headers $headers -Body (ConvertTo-Json $sessionRequest) -Method Post
        $sessionId = $createSessionResponse.sessionId

        # Wait for the session to start
        while (-not (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/isReady").value)
        {
            # Get detailed status information
            $status = (Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/status").value

            # Break this loop if Status is DeployFailed or be stuck forever
            if($status.Status -eq "DeployFailed")
            {
                Throw "Deploy session failed: $($status.StatusMessage)"
            }
            # Filter duplicate messages in the output, unless verbose flag is set
            if(($statusOld -ne $status.Status) -or ($statusMessageOld -ne $status.StatusMessage))
            {
                Write-Host "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
                $statusOld = $status.Status
                $statusMessageOld = $status.StatusMessage
            }
            else
            {
                Write-Verbose "The session $($sessionId) is $($status.Status): $($status.StatusMessage)"
            }
            Start-Sleep -Milliseconds 1000
        }

        if ($script:currentSessionId -ne $null)
        {
            Write-Warning "Overriding the currently set default session"
        }
        else
        {
            Write-Information "Using this session as your default session"
        }

        $script:currentSessionId = $sessionId

        return $sessionId
    }
}

<#
.SYNOPSIS
Adds an application to the application repository
 
.DESCRIPTION
Adds an application to the application repository
 
.EXAMPLE
Add-App -path "c:\apps\quamotion.apk"
#>

function Add-App
{
    param ([string] $path)

    Add-Type -AssemblyName System.Net.Http

    $fullPath = Resolve-Path $path
    if(-not (Test-Path $fullPath))
    {
        Throw "The file at $fullPath could not be found"
    }

    $ContentType = "application/octet-stream"
    $fileBin = [IO.File]::ReadAllBytes($fullPath)
    $fileName = Split-Path $path -leaf
    $boundary = [guid]::NewGuid().ToString()

    $httpClientHandler = New-Object System.Net.Http.HttpClientHandler

    $httpClient = New-Object System.Net.Http.Httpclient $httpClientHandler

    $packageFileStream = New-Object System.IO.FileStream @($path, [System.IO.FileMode]::Open)

    $contentDispositionHeaderValue = New-Object System.Net.Http.Headers.ContentDispositionHeaderValue "form-data"
    $contentDispositionHeaderValue.Name = "name"
    $contentDispositionHeaderValue.FileName = (Split-Path $path -leaf)

    $streamContent = New-Object System.Net.Http.StreamContent $packageFileStream
    $streamContent.Headers.ContentDisposition = $contentDispositionHeaderValue
    $streamContent.Headers.ContentType = New-Object System.Net.Http.Headers.MediaTypeHeaderValue $ContentType

    $content = New-Object System.Net.Http.MultipartFormDataContent
    $content.Add($streamContent)

    $result = $httpClient.PostAsync("$prefix/quamotion/app", $content).Result

    $response = $result.Content.ReadAsStringAsync().Result
    $response = ConvertFrom-Json $response
    if($result.StatusCode -eq "200")
    {
        return $response
    }
    else
    {
        Write-Error("Adding app Failed: " + $response)
    }
}

<#
.SYNOPSIS
Gets all applications in the application repository.
 
.DESCRIPTION
Gets all applications in the application repository.
 
.EXAMPLE
Get-App
#>

function Get-App
{
    Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/app"
}

<#
.SYNOPSIS
Gets all available devices.
 
.DESCRIPTION
Gets all available devices
 
.EXAMPLE
Get-Device
#>

function Get-Device
{
    param ([string] $deviceId)

    if ($deviceId)
    {
        Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device/$deviceId"
    }
    else
    {
        Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device"
    }
}

<#
.SYNOPSIS
Waits until a particular device is available before continuing to run.
 
.DESCRIPTION
The Wait-Device cmdlet suspends execution of a script or function until a particular device is available.
Execution resumes when the device is detected. To cancel the wait, press CTRL+C.
 
.PARAMETER deviceId
The identifier of the device for which to wait.
 
.PARAMETER timeout
Specifies the maximum time, in seconds, that Wait-Devices waits for the event to become available.
The default, -1, waits indefinitely. The timing starts when you submit the Wait-Device command.
 
If the specified time is exceeded, the wait ends and the command prompt returns, even if the device is not available.
No error message is displayed.
 
.EXAMPLE
Wait-Device -deviceId 9e8438ab23a10a20cd2d4d8d9616b405d008344f
#>

function Wait-Device
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [int] $timeout = -1)

    if ($timeout > -1)
    {
        $endDate = (Get-Date).AddSeconds($timeout)
    }
    else
    {
        $endDate = [System.DateTime]::MaxValue
    }

    $device = $null

    do
    {
        try
        {
            $device = Get-Device -deviceId $deviceId
        }
        catch
        {
            # The device is not available, sleep.
            Start-Sleep -Milliseconds 250
        }
    } while (!$device -and $endDate -gt (Get-Date))
}

function Get-DeviceRotation
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId)

    Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device/$deviceId/rotation"
}

function Install-App
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [string] $appId,
        [Parameter(Mandatory=$false)] [string] $appVersion)
    if($appVersion)
    {
        Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/app/$appId/$appVersion" -TimeoutSec 3600 -Method Post
    }
    else
    {
        Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/app/$appId" -TimeoutSec 3600 -Method Post
    }
}

<#
.SYNOPSIS
Gets all installed applications on the device.
 
.DESCRIPTION
Gets all installed applications on the device.
 
.PARAMETER deviceId
The identifier of the device.
 
.EXAMPLE
Get-InstalledApp -deviceId $iPhone6
#>

function Get-InstalledApp
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId)

    $apps = (Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device/$deviceId/app" -TimeoutSec 3600 -Method Get)
    return $apps
}

<#
.SYNOPSIS
Gets the version of an installed application.
 
.DESCRIPTION
Gets the version of an installed application.
 
.PARAMETER deviceId
The identifier of the device.
 
.PARAMETER appId
The identifier of the application.
 
.EXAMPLE
Get-InstalledAppVersion -deviceId $iPhone6 -appId "mobi.quamotion.Acquaint"
#>

function Get-AppVersion
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [string] $appId)

    $version = (Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device/$deviceId/app/$appId/version" -TimeoutSec 3600 -Method Get)
    return $version
}

<#
.SYNOPSIS
Lists all processes which are running on a device.
 
.DESCRIPTION
Lists all processes which are running on a device.
 
.PARAMETER deviceId
The identifier of the device on which to list all the processes which are running.
 
.EXAMPLE
Get-DeviceProcess -deviceId $iPhone6
#>

function Get-DeviceProcess
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId)

    $apps = (Invoke-WebDriverRestMethod -Uri "$prefix/quamotion/device/$deviceId/process" -TimeoutSec 3600 -Method Get)
    return $apps
}

<#
.SYNOPSIS
Stops a processing running on a device.
 
.DESCRIPTION
Stops a process running on a device.
 
.PARAMETER deviceId
The device on which the process is running.
 
.PARAMETER appId
The process ID of the process to stop.
 
.EXAMPLE
Stop-DeviceProcess -deviceId $iPhone6 -processId 604
#>

function Stop-DeviceProcess
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [int] $processId
        )
    
    Invoke-WebDriverRestMethod "$prefix/quamotion/device/$deviceId/process/$processId" -Headers $headers -Method Delete
}

<#
.SYNOPSIS
Stops an application running on a device.
 
.DESCRIPTION
Stops an application running on a device.
 
.PARAMETER deviceId
The device on which the application is running.
 
.PARAMETER appId
The application ID of the application to stop.
 
.EXAMPLE
Stop-InstalledApp -deviceId $iPhone6 -appId mobi.quamotion.Acquaint
#>

function Stop-InstalledApp
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [string] $appId
        )
    
    Invoke-WebDriverRestMethod "$prefix/quamotion/device/$deviceId/app/$appId/kill?strict" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Uninstalls an application from a device.
 
.DESCRIPTION
Uninstalls an application from a device.
 
.PARAMETER deviceId
The identifier of the device.
 
.PARAMETER appId
The identifier of the application.
 
.EXAMPLE
Uninstall-App -deviceId $iPhone6 -appId "demo.quamotion.Acquaint"
#>

function Uninstall-App
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [string] $appId)

    Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/app/$appId" -TimeoutSec 3600 -Method Delete
}

<#
.SYNOPSIS
Gets the orientiation of a device.
 
.DESCRIPTION
Gets the orientiation of the device used in a session.
Returns: the orientation of the device.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-Orientation
#>

function Get-Orientation
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    return (Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/orientation" -TimeoutSec 3600 -Method Get).Content
}

<#
.SYNOPSIS
Sets the orientiation of a device.
 
.DESCRIPTION
Sets the orientiation of the device used in a session.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-Orientation
#>

function Set-Orientation
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $orientation)

    Test-SessionId $sessionId

    $orientationRequest = @{
        orientation = $orientation
    }

    Write-Verbose "Setting orientation to $orientation"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/orientation" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $orientationRequest)))  -Method Post
}

function Set-DeviceOrientation
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [double] $alpha,
        [Parameter(Mandatory=$true, Position = 2)] [double] $beta,
        [Parameter(Mandatory=$true, Position = 3)] [double] $gamma)

    Test-SessionId $sessionId

    $deviceOrientationRequest = @{
        alpha = $alpha
        beta = $beta
        gamma = $gamma
    }

    Write-Verbose "Setting device orientation to ($alpha, $beta, $gamma)"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/deviceOrientation" -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $deviceOrientationRequest)))  -Method Post
}

function Reset-DeviceOrientation
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    Write-Verbose "Setting device orientation to ($alpha, $beta, $gamma)"
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/deviceOrientation" -Headers $headers -Method Delete
}

<#
.SYNOPSIS
Accepts the visible alert window.
 
.DESCRIPTION
Accepts the visible alert window.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Accept-Alert
#>

function Accept-Alert
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    $response = Invoke-WebDriverRestMethod -Uri "$prefix/session/$sessionId/accept_alert" -TimeoutSec 3600 -Method Post
}

<#
.SYNOPSIS
Dismiss the visible alert window.
 
.DESCRIPTION
Dismiss the visible alert window.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Dismiss-Alert
#>

function Dismiss-Alert
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    $response = Invoke-WebDriverRestMethod -Uri "$prefix/session/$sessionId/dismiss_alert" -TimeoutSec 3600 -Method Post
}

<#
.SYNOPSIS
Clicks on an alert button.
 
.DESCRIPTION
Clicks on an alert button.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER button
The name of the button to click
 
.EXAMPLE
Click-AlertButton -button "Ok"
#>

function Click-AlertButton
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory = $true, Position = 1)] [string] $button)

    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod -Uri "$prefix/session/$sessionId/alert/click/$button" -TimeoutSec 3600 -Method Post
}

<#
.SYNOPSIS
Gets all buttons of the visible alert window.
 
.DESCRIPTION
Gets all buttons of the visible alert window.
Returns: all available button names.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-AlertButtons
#>

function Get-AlertButtons
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    (Invoke-WebDriverRestMethod -Uri "$prefix/session/$sessionId/alert/buttons" -TimeoutSec 3600 -Method Get).value
}

<#
.SYNOPSIS
Report a status message.
 
.DESCRIPTION
Report a status message.
The message will appear in the test-log.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER success
Indicates whether the status is success or failure
 
.PARAMETER message
The message.
 
.EXAMPLE
report_status -success $false "Something went wrong"
#>

function Report-Status
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true)] [bool] $success,
        [Parameter(Mandatory=$true)] [string] $message)

    Test-SessionId $sessionId

    $reportStatusRequest = @{
        Success = $success;
        Message = $message;
    }
    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/report_status" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $reportStatusRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Dismisses an open keyboard
 
.DESCRIPTION
Dismisses an open keyboard
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Dismiss-Keyboard
#>

function Dismiss-Keyboard
{
    param (
        [string] $sessionId = $script:currentSessionId
    )

    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/dismissKeyboard" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Searches for an element matching a given locator value.
 
.DESCRIPTION
Searches for an element matching the locator value.
Returns: the first element matching the locator value.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER xpath
(xpath locator) The value for the xpath locator
 
.PARAMETER class
(class locator) The value for the class locator
 
.PARAMETER marked
(Marked locator) The value for the marked locator
 
.EXAMPLE
Find-ElementFlex -marked "Login"
Find-ElementFlex -xpath "*[@marked='Login']"
Find-ElementFlex -class "UIView"
#>

function Find-ElementFlex
{
    param (
        [Parameter(ParameterSetName = "Marked")]
        [Parameter(ParameterSetName = "XPath")]
        [Parameter(ParameterSetName = "Class")]
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, ParameterSetName = "XPath")]
        [string] $xpath,
        [Parameter(Mandatory=$true, ParameterSetName = "Class")]
        [string] $class,
        [Parameter(Mandatory=$true, ParameterSetName = "Marked")]
        [string] $marked)

    Test-SessionId $sessionId

    if ($xpath)
    {
        return Find-Element -sessionId $sessionId -xpath $xpath
    }
    elseif($marked)
    {
        return Find-Element -sessionId $sessionId -xpath "//*[@marked='$marked']"
    }
    elseif($class)
    {
         Write-Verbose "Finding element //$class"
        return  Find-Element -sessionId $sessionId -xpath "//$class"
    }
}

<#
.SYNOPSIS
Scrolls until the element is visible
 
.DESCRIPTION
Scrolls until the element is visible. Currently only implemented for Full Device Automation.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the element to become visible.
 
.EXAMPLE
Scroll-ToVisible -elementId $element
#>

function Scroll-ToVisible
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [string] $elementId)

    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/quamotion/scrollToVisible" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Scrolls until an element with a matching locator value is visible
 
.DESCRIPTION
Scrolls until an element with a matching locator value is visible.
Note that the element should already be loaded.
Please use the ScollDown-To or ScrollUp-To commands if the application generates the list dynamically.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the scroll view.
 
.PARAMETER marked
(marked locator) The value for the marked locator
 
.PARAMETER xpath
(xpath locator) The value for the xpath locator
 
.EXAMPLE
Scroll-To -elementId $scrollView -marked "John"
#>

function Scroll-To
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [string] $elementId,
        [string] $marked,
        [string] $xpath)

    Test-SessionId $sessionId

    if($marked)
    {
        $scrollToRequest = @{
            using = "xpath";
            value = "//*[@marked='$marked']";
        }
    }
    else
    {
        $scrollToRequest = @{
            using = "xpath";
            value = $xpath;
        }
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/quamotion/scrollTo" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $scrollToRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Scrolls down until an element with a matching xpath is visible
 
.DESCRIPTION
Scrolls down until an element with a matching xpath is visible
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the scroll view.
 
.PARAMETER xpath
(marked locator) The value for the marked locator
 
.EXAMPLE
ScrollDown-To -elementId $scrollView -xpath "*[@marked='John']"
#>

function ScrollDown-To
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [string] $elementId,
        [string] $xpath,
        [int] $scrollTimeout)

    Test-SessionId $sessionId

    $scrollToRequest = @{
        using = "xpath";
        value = $xpath;
        direction = "Down";
        scrollTimeout = $scrollTimeout;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/quamotion/scrollTo" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $scrollToRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Scrolls up until an element with a matching xpath is visible
 
.DESCRIPTION
Scrolls up until an element with a matching xpath is visible
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier of the scroll view.
 
.PARAMETER xpath
(marked locator) The value for the marked locator
 
.EXAMPLE
ScrollUp-To -elementId $scrollView -xpath "*[@marked='John']"
#>

function ScrollUp-To
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [string] $elementId,
        [string] $xpath,
        [int] $scrollTimeout)

    Test-SessionId $sessionId

    $scrollToRequest = @{
        using = "xpath";
        value = $xpath;
        direction = "Up";
        scrollTimeout = $scrollTimeout;
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/quamotion/scrollTo" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $scrollToRequest))) -Headers $headers -Method Post
}

function Set-Value
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(ValueFromPipeline=$True)]
        [string] $elementId,
        [string] $value)

    Test-SessionId $sessionId

    $setValueRequest = @{
        value = @($value)
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/value" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $setValueRequest))) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Clears the active text element
 
.DESCRIPTION
Clears the active text element
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Clear-Text
#>

function Clear-Text
{
    param (
        [string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/quamotion/clear" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Clears the text of an element
 
.DESCRIPTION
Clears the text of an element
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The ID of the element to clear
 
.EXAMPLE
Clear-ElementText -elementId $elementId
#>

function Clear-ElementText
{
    param(
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory = $true)] [string] $elementId)

    Test-SessionId $sessionId

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/clear" -Headers $headers -Method Post
}

<#
.SYNOPSIS
Gets the text displayed on the alert window.
 
.DESCRIPTION
Gets the text displayed on the alert window.
Returns: the displayed alert window text
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-AlertText
#>

function Get-AlertText
{
    param ([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    $content = (Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/alert/text" -TimeoutSec 3600 -Method Get).Content
    $json = ConvertFrom-Json($content)
    return $json.value
}

<#
.SYNOPSIS
Executes JavaScript in the browser.
 
.DESCRIPTION
Executes JavaScript in the browser.
Returns: the value returned by the JavaScript script.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER script
The JavaScript to execute.
 
.EXAMPLE
Execute-Script -script "window.alert('Hello, World!');"
#>

function Execute-Script
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [string] $script)

    Test-SessionId $sessionId

    $executeScriptRequest = @{
        script = $script
    }

    $json = (ConvertTo-Json $executeScriptRequest)

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/execute/sync" -Body ([System.Text.Encoding]::UTF8.GetBytes($json)) -Headers $headers -Method Post
}

<#
.SYNOPSIS
Deletes the application settings of an installed application.
 
.DESCRIPTION
Deletes the application settings of an installed application.
 
.PARAMETER appId
The application identifier
 
.PARAMETER deviceId
The device identifier
 
.EXAMPLE
Delete-AppSettings -appId "demo.quamotion.Acquaint" -deviceId "$iPhone6"
#>

function Delete-AppSettings
{
    param (
        [Parameter(Mandatory=$true)] [string] $deviceId,
        [Parameter(Mandatory=$true)] [string] $appId)

    Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/app/$appId/settings" -TimeoutSec 3600 -Method Delete
}

<#
.SYNOPSIS
Reboots a device.
 
.DESCRIPTION
Reboots a device.
 
.PARAMETER deviceId
The device identifier
 
.EXAMPLE
Reboot-Device -deviceId $iPhone6
#>

function Reboot-Device
{
    param ([Parameter(Mandatory=$true)] [string] $deviceId)


    Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/reboot" -TimeoutSec 3600 -Method Post
}

<#
.SYNOPSIS
Tests whether the element is visible.
 
.DESCRIPTION
Tests whether the element is visible.
$true or $false depending of the visibility of the element.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER elementId
The identifier element.
 
.EXAMPLE
Is-Displayed -elementId $loginButton
#>

function Is-Displayed
{
    param(
        [string] $sessionId = $script:currentSessionId, 
        [Parameter(Mandatory = $true, Position = 1)] [string] $elementId)

    Test-SessionId $sessionId

    $response = Invoke-WebDriverRestMethod "$prefix/session/$sessionId/element/$elementId/displayed" -Method Get -Headers $headers
    return $response.value
}

<#
.SYNOPSIS
Takes a screenshot of the device used in the given session.
 
.DESCRIPTION
Takes a screenshot of the device used in the given session.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER filePath
The file path used to store the screenshot.
 
.EXAMPLE
Get-Screenshot -filePath c:\temp\screenhot.jpg
#>

function Get-Screenshot
{
    param([string] $sessionId = $script:currentSessionId, [string] $filePath)

    Test-SessionId $sessionId

    $screenshot = Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/screenshot" -TimeoutSec 3600 -Method Get
    $jsonScreenshot = ConvertFrom-Json $screenshot.Content
    $bytes=[System.Convert]::FromBase64String($jsonScreenshot.value)
    $decodedScreenshot=[System.Text.Encoding]::Default.GetString($bytes)
    Set-Content $filePath -value $decodedScreenshot
}

<#
.SYNOPSIS
Set the amount of time, in milliseconds, are permitted to run before they are aborted and a timeout error is returned to the client.
 
.DESCRIPTION
Set the amount of time, in milliseconds, are permitted to run before they are aborted and a timeout error is returned to the client.
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.PARAMETER time
The amount of time to wait, in milliseconds. This value has a lower bound of 0.
 
.EXAMPLE
Set-Timeout -time 3000
#>

function Set-Timeout
{
    param (
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $time)

    Test-SessionId $sessionId

    $timeout = @{
        implicit = $time;
    }

    $result = Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/timeouts" -Headers $headers -Body (ConvertTo-Json $timeout) -Method Post
}

<#
.SYNOPSIS
Gets the timeouts for the given session
 
.DESCRIPTION
Gets the timeouts for the given session
 
.PARAMETER sessionId
(Optional) The identifier of the session to route the command to.
 
.EXAMPLE
Get-Timeouts
#>

function Get-Timeouts
{
    param (
        [string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    $content = (Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/timeouts" -Method Get).Content
    $json = ConvertFrom-Json($content)
    return $json.value
}

<#
.SYNOPSIS
Gets the battery info of a device.
 
.DESCRIPTION
Gets the battery info of a device.
Returns: the battery info of a device.
 
.PARAMETER deviceId
The device identifier.
 
.EXAMPLE
Get-BatteryInfo -deviceId $iPhone6
#>

function Get-BatteryInfo
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId)

    return (Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/battery" -TimeoutSec 3600 -Method Get).Content
}

<#
.SYNOPSIS
Gets the navigation timing of the current webview window.
 
.DESCRIPTION
Gets the navigation timing of the current webview window.
 
.EXAMPLE
Get-PerformanceData
#>

function Get-PerformanceData
{
    param([string] $sessionId = $script:currentSessionId)

    Test-SessionId $sessionId

    return (Invoke-WebDriverWebRequest -Uri "$prefix/session/$sessionId/quamotion/performance" -TimeoutSec 3600 -Method Get).Content
}

<#
.SYNOPSIS
Gets the disk info of a device.
 
.DESCRIPTION
Gets the disk info of a device.
Returns: the disk info of a device.
 
.PARAMETER deviceId
The device identifier.
 
.EXAMPLE
Get-DiskInfo -deviceId $iPhone6
#>

function Get-DiskInfo
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId)

    return (Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/disk" -TimeoutSec 3600 -Method Get).Content
}

<#
.SYNOPSIS
Gets the domain settings of a device.
 
.DESCRIPTION
Gets the domain settings of a device.
Returns: the domain settings of a device.
 
.PARAMETER deviceId
The device identifier.
 
.PARAMETER domain
The settings domain.
 
.EXAMPLE
Get-DeviceSettings -deviceId $iPhone6 -domain "global"
#>

function Get-DeviceSettings
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $domain)

    $settings = (Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/settings/$domain" -TimeoutSec 3600 -Method Get).Content
    return $settings| ConvertFrom-json
}

<#
.SYNOPSIS
Gets a setting of a device.
 
.DESCRIPTION
Gets a settings of a device.
Returns: the settings of the device.
 
.PARAMETER deviceId
The device identifier.
 
.PARAMETER domain
The settings domain.
 
.PARAMETER key
The settings key.
 
.EXAMPLE
Get-DeviceSetting -deviceId $iPhone6 -domain "global" -key "ota_disable_automatic_update"
#>

function Get-DeviceSetting
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $domain,
        [Parameter(Mandatory=$true, Position = 3)] [string] $key
    )

    $settings = (Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/settings/$domain/$key" -TimeoutSec 3600 -Method Get).Content
    return $settings| ConvertFrom-json
}


<#
.SYNOPSIS
Set a settings of a device.
 
.DESCRIPTION
Sets a settings of a device.
 
.PARAMETER deviceId
The device identifier.
 
.PARAMETER domain
The settings domain.
 
.PARAMETER key
The settings key.
 
.PARAMETER value
The value to set.
 
.EXAMPLE
Set-DeviceSetting -deviceId $iPhone6 -domain "global" -key "ota_disable_automatic_update" -value "0"
#>

function Set-DeviceSetting
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $domain,
        [Parameter(Mandatory=$true, Position = 3)] [string] $key,
        [Parameter(Mandatory=$true, Position = 4)] [string] $value
    )
    $request = @{
        value = $value
    }

    $settings = (Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/settings/$domain/$key" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $value))) -Headers $headers -Method Post).Content
    return $settings| ConvertFrom-json
}

<#
.SYNOPSIS
Updates the geolocation of the device on which the test is currently running.
 
.PARAMETER latitude
The new latitude.
 
.PARAMETER longitude
The new longitude.
 
.EXAMPLE
Set-GeoLocation 50.838061 4.841535
Set-Geolocation 50.826296 4.399623
Set-GeoLocation 50.983377 3.686608
#>

function Set-GeoLocation
{
    param(
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $latitude,
        [Parameter(Mandatory=$true, Position = 21)] [string] $longitude)

    Test-SessionId $sessionId

    $setLocationRequest = @{
        latitude = $latitude
        longitude = $longitude
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/location" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $setLocationRequest))) -Headers $headers -Method Post
}

function Start-App
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId,
        [Parameter(Mandatory=$true, Position = 2)] [string] $appId,
        [Parameter(Mandatory=$false, Position = 3)] [string[]] $arguments
    )

    $url = "$prefix/quamotion/device/$deviceId/app/$appId/launch?strict"
    if($arguments)
    {
        Invoke-WebDriverRestMethod $url -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $arguments -Compress))) -Headers $headers
    }
    else
    {
        Invoke-WebDriverRestMethod $url -Method Post -Headers $headers -Body "null" -ContentType "application/json"
    }
}

function Stop-Agent
{
    param(
        [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId
    )

    Invoke-WebDriverRestMethod "$prefix/quamotion/device/$deviceId/stopAgent" -Method Post
}

function Enable-LocationServices
{
   param(
   [Parameter(Mandatory=$true, Position = 1)] [string] $deviceId,
   [Parameter(Mandatory=$true, Position = 2)] [string[]] $locationServices)

   $locationServiceRequest = @{
       location_services = $locationServices;
}

   $result = Invoke-WebDriverWebRequest -Uri "$prefix/quamotion/device/$deviceId/location_services" -Headers $headers -Body (ConvertTo-Json $locationServiceRequest) -Method Post
}

function Invoke-WebDriverRestMethod
{
    param()

    try
    {
        return Invoke-RestMethod @args -ErrorAction Stop
    }
    catch
    {
        Rethrow-Exception $_
    }
}

function Invoke-WebDriverWebRequest
{
    param()

    try
    {

        Invoke-WebRequest @args -ErrorAction Stop
    }
    catch
    {
        Rethrow-Exception $_
    }
}

# Shared error handling
function Rethrow-Exception($originalError)
{
    $errorMessage = Get-ErrorMessage($originalError)
    if($errorMessage)
    {
        $status = $errorMessage.status
        $message = $errorMessage.value.message
        try
        {
            $jsonMessage = $message | ConvertFrom-Json
            
            if($jsonMessage.description)
            {
                $message = $jsonMessage.description
            }
        }
        catch
        {
        }

        Throw (New-Object WebDriverException $message,$status)
    }
    else 
    {
        Throw $exception
    }
}

Function Get-ErrorMessage ($error) {

# If something went wrong in the HTTP pipeline, the result may not be JSON, or we may not have a
    # response at all (if the server was unavailable)

    # PowerShell "Core" stores error details in the ErrorDetails variable. We first check if this info exists, see
    # https://github.com/PowerShell/PowerShell/issues/5555
    # https://github.com/PowerShell/PowerShell/pull/3089
    $errorDetails = $error.ErrorDetails
    $exception = $error.Exception
    if($errorDetails)
    {
        $errorMessage = $errorDetails.Message | ConvertFrom-Json
        return $errorMessage
    }
    elseif ($exception.Response -and $exception.Response.ContentType -and $exception.Response.ContentType.StartsWith("application/json"))
    {
        $result = $exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();

        $errorMessage = ConvertFrom-Json $responseBody
        return $errorMessage
    }
    else
    {
        Throw $exception
    }
}

<#
.SYNOPSIS
Gets the session log of a specific session.
 
.DESCRIPTION
Gets the session log of a specific session.
Returns: The session log as JSON.
 
.PARAMETER SessionId
(Optional) The session identifier.
 
.EXAMPLE
Get-SessionLog -SessionId 0f59edb6-914d-4e24-b77d-dbcfb97008d6
#>

function Get-SessionLog
{
    param(
        [string] $sessionId = $script:currentSessionId)

        $result = Invoke-WebRequest -Uri "http://localhost:17894/Dashboard/SessionLog?sessionid=$sessionId" -Headers $headers -Method GET
        $testLogNumber = $result.BaseResponse.ResponseUri.Segments.Get($result.BaseResponse.ResponseUri.Segments.Count-1)
        Write-Host "testLogNumber $testLogNumber"

        $testLog = Invoke-RestMethod -Uri "$prefix/quamotion/testlog/$testLogNumber/export" -Headers $headers -Method GET
        return ConvertTo-Json $testLog
}

<#
.SYNOPSIS
Removes an application from the application repository.
 
.DESCRIPTION
Removes an application from the application repository.
 
.EXAMPLE
Remove-App -operatingSystem "iOS" -appVersion "1.51" -appId "demo.quamotion.Acquaint"
#>

function Remove-App
{
    param ([string] $operatingSystem, [string] $appId, [string] $appVersion)
    $result = Invoke-WebDriverRestMethod "$prefix/quamotion/app/$operatingSystem/$appId/$appVersion" -Method Delete
    return $result
}

function Execute-Script
{
    param(
        [string] $sessionId = $script:currentSessionId,
        [Parameter(Mandatory=$true, Position = 1)] [string] $script,
        [Parameter(Mandatory=$true, Position = 21)] [object[]] $args)

    $executeScriptRequest = @{
        script = $script
        args = $args
    }

    Invoke-WebDriverRestMethod "$prefix/session/$sessionId/execute/sync" -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $executeScriptRequest))) -Headers $headers -Method Post
}

function Test-SessionId
{
    param(
        [string] $sessionId
    )

    if(-not $sessionId)
    {
        Throw "You must specify a valid session id. Did you run Start-Session, Start-DeviceSession or Start-WebSession?"
    }
}

Class WebDriverException : System.Exception
{
    [int] $Status = 0
    WebDriverException([string] $message, [int] $status) : base($message)
    {
        $this.Status = $status
    }
}

Enum LocatorStrategy
{
 ClassName 
 CssSelector
 Id
 Name
 LinkText 
 PartialLinkText
 TagName
 XPath
 PredicateString
 ClassChain
}