Module.psm1

#Requires -Version 5.1

$ErrorActionPreference = "Stop"

# Only suported on Windows PowerShell. For the time being.
if ($IsCoreCLR) { Throw "Due to limitations with the Selenium PowerShell Module. This module is not supported on PowerShell Core yet." }

Write-Host "Selenium MVP Module Loaded. Use 'ConnectTo-MVPPortal' to get started." -ForegroundColor Green

#CompiledByBuildScript
#Resource: 00_HTMLElements.ps1
Data LocalizedDataHTMLElements {
    ConvertFrom-StringData @'
    Date=DateOfActivity
    Title=TitleOfActivity
    URL=ReferenceUrl
    Description=Description
    AnnualQuantity=AnnualQuantity
    SecondAnnualQuantity=SecondAnnualQuantity
    AnnualReach=AnnualReach
'@

}

#Resource: 01_HTMLFormStrucuture.ps1
# Read-Only
New-Variable -Name HTMLFormStructure -Scope Global -Option ReadOnly -Force -Value @(
    @{
        Name = "Default"
        Properties = @(
            @{
                Name = 'Date'
                Element = $LocalizedDataHTMLElements.Date
                isRequired = $true
            }
            @{
                Name = 'Title'
                Element = $LocalizedDataHTMLElements.Title
                isRequired = $true
            }
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $false
            }
            @{
                Name = 'Description'
                Element = $LocalizedDataHTMLElements.Description
            }            
        )
    }
    @{
        Name = "Article"
        Properties = @(
            @{
                Name = 'Number of Articles'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Number of Views'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }           
        )
    }
    @{
        Name = "Blog/WebSite Post"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            }
            @{
                Name = 'Number of Posts'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Number of Subscribers'
                Element = $LocalizedDataHTMLElements.SecondAnnualQuantity
            },
            @{
                Name = 'Annual Unique Visitors'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                       
        )
    }
    @{
        Name = "Book (Author)"
        Properties = @(
            @{
                Name = 'Number of Books'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Copies Sold'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                       
        )
    }
    @{
        Name = "Book (Co-Author)"
        Properties = @(
            @{
                Name = 'Number of Books'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Copies Sold'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                       
        )
    }
    @{
        Name = "Conference (Staffing)"
        Properties = @(
            @{
                Name = 'Number of Conferences'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Number of Visitors'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                       
        )
    }
    @{
        Name = "Docs.Microsoft.com Contribution"
        Properties = @(
            @{
                Name = 'Pull Requests/Issues/Submissions'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }                     
        )
    }
    @{
        Name = "Forum Moderator"
        Properties = @(
            @{
                Name = 'Number of Threads moderated'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }                     
        )
    }
    @{
        Name = "Forum Participation (3rd Party forums)"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            }
            @{
                Name = 'Number of Answers'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Number of Posts'
                Element = $LocalizedDataHTMLElements.SecondAnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Views of answers'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                   
        )
    }
    @{
        Name = "Forum Participation (Microsoft Forums)"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            },            
            @{
                Name = 'Number of Answers'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            },
            @{
                Name = 'Number of Posts'
                Element = $LocalizedDataHTMLElements.SecondAnnualQuantity
            },
            @{
                Name = 'Views of answers'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                   
        )
    }
    @{
        Name = "Mentorship"
        Properties = @(
            @{
                Name = 'Mentoring Topic'
                Element = $LocalizedDataHTMLElements.Description
            }             
            @{
                Name = 'Number of Mentorship Activity'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Mentees'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                 
        )
    }
    @{
        Name = "Microsoft Open Source Projects"
        Properties = @(           
            @{
                Name = 'Number of Projects'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }                             
        )
    }
    @{
        Name = "Non-Microsoft Open Source Projects"
        Properties = @(
            @{
                Name = 'Projects(s)'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Contribution(s)'
                Element = $LocalizedDataHTMLElements.SecondAnnualQuantity
            }                                            
        )
    }
    @{
        Name = "Organizer (User Group/Meetup/Local Events)"
        Properties = @(
            @{
                Name = 'Meetings'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Members'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Organizer of Conference"
        Properties = @(
            @{
                Name = 'Number of Conferences'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Attendees'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Other"
        Properties = @(
            @{
                Name = 'Annual quantity'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Annual reach'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }  
    @{
        Name = "Product Group Feedback"
        Properties = @(
            @{
                Name = 'Number of Events Participated'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Feedbacks Provided'
                Element = $LocalizedDataHTMLElements.SecondAnnualQuantity
            }                                            
        )
    }
    @{
        Name = "Sample Code/Projects/Tools"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            }            
            @{
                Name = 'Number of Samples'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Downloads'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Site Owner"
        Properties = @(          
            @{
                Name = 'Number of Sites'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Visitors'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Speaking (Conference)"
        Properties = @(          
            @{
                Name = 'Number of Talks'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Attendees of Talks'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Speaking (User Group/Meetup/Local events)"
        Properties = @(          
            @{
                Name = 'Number of Talks'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Attendees of Talks'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Technical Social Media (Twitter, Facebook, LinkedIn...)"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            }                    
            @{
                Name = 'Number of Talks'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Followers'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Translation Review, Feedback and Editing"
        Properties = @(          
            @{
                Name = 'Annual quantity'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }                                         
        )
    }
    @{
        Name = "Video/Webcast/Podcast"
        Properties = @(
            @{
                Name = 'URL'
                Element = $LocalizedDataHTMLElements.URL
                isRequired = $true
            }                   
            @{
                Name = 'Number of Videos'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }
            @{
                Name = 'Number of Views'
                Element = $LocalizedDataHTMLElements.AnnualReach
            }                                            
        )
    }
    @{
        Name = "Workshop/Volunteer/Proctor"
        Properties = @(          
            @{
                Name = 'Number of Events'
                Element = $LocalizedDataHTMLElements.AnnualQuantity
                isRequired = $true
            }                                          
        )
    }                       
)
#Resource: 02_HTMLFormStructureFormatting.ps1
New-Variable -Name HTMLFormStructureValidate -Scope Global -Option ReadOnly -Force -Value @(

    @{
        Name = "Date"
        Format = {
            param($date)
            $currentCultureDate = [DateTime]::Parse($date, [cultureinfo]::New((Get-Culture).Name))                    
            write-output ($currentCultureDate.ToString("MM/dd/yyyy"))
        }
    }

    @{
        Name = "URL"
        Format = {
            param($url)
            Write-Output ([URI]::New($url).AbsoluteUri)
        }
    }    

)
#Resource: 03_LocalizedData.ps1
data LocalizedData {
    ConvertFrom-StringData @'
    ErrorConnectToMVPPortal=An Error occured when attempting to connect to the portal. Details: '{0}'
    ErrorNestedMVPActivity=Nested MVPActivity. MVPActivity is only requried for the top-level declaration. Refer to usage.
    ErrorMissingMVPActivityArea=Missing 'Area' Statement.
    ErrorMissingMVPActivityAreaMultiple=Multiple Statements of 'Area' was detected. Only a single instance is permitted.
    ErrorMissingMVPActivityContributionArea=Missing 'ContributionArea' Statement.
    ErrorExceedMVPActivityContributionArea=Exceeded 'ContributionArea' Limit. Limit is 2. Count was '{0}'.
    ErrorMissingMVPActivityValue=Missing 'Value' Statement.
    ErrorMissingActivityType=Unable to enumerate ActivityTypes from source HTML.
    ErrorMissingContributionType=Unable to enumerate ContributionAreas from source HTML.
    ErrorMissingDriver=Missing Selinum Driver. Use: ConnectTo-Selenium to connect.
    ErrorTryTentitiveCommand=Try-TentativeCommand: Exceeded Retry Limit.
    ErrorMissingSelectedValue=The Parameter '-SelectedValue' value '{0}', could not be found
    ErrorTooManySelectedValue=The Parameter '-SelectedValue' value '{0}', returned too many items. Count '{1}'
    ErrorJavaScriptTimeout=Javascript Timeout.
    ErrorNoActivityButton=Cannot Select Add New Activity Button.
    ErrorAreaNotNested=Error. 'Area' is not nested within MVPActivity.
    ErrorContributionAreaNotNested=Error. 'ContributionArea' is not nested within MVPActivity.
    ErrorHTMLFormStructureDefaultParameter=Error. The 'default' Parameter Value is prohibited.
    ErrorHTMLFormStructureMissingName=Error. Could not match name '{0}' with HTMLFormStructure.
    ErrorCannotFindHTMLElement=Error. Could not match HTML element '{0}' with HTMLFormStructure.
    ErrorTooManyHTMLElements=Error. Too many results with HTML element '{0} with HTMLFormStructure. Count '{1}'
    ErrorFieldValidationError=Error. A field validation error was found within the form. Error: '{0}'"
    ErrorMissingRequiredEntries=Error. Missing Required HTML Fields: '{0}'
    ErrorMissingMVPActivityInCallStack=Error. '{0}' can only be called from within MVPActivity.
    ErrorFormattingValue=Error. There was an error raised when attempting to format '{0}' with '{1}'. Error Message: {2}
    ErrorSavingMVPActivity=Error. There was an error raised when attempting to save the MVP activity. Error: '{0}'
    ErrorSelectingDropDown=Error. There was an error raised when attempting to select the dropDown '{0}' with the value: '{1}'. Error: '{2}'
    ErrorStopMVPActivity=Error. There was an error raised when attempting to cancel the MVP activity. Error: '{0}'
    ErrorAreaFailure=Error. Could not set MVP Area within HTML Form, unable to continue.
    ErrorTestCSVSchemaMissingCSVFile="Missing CSV File: '{0}'"
    ErrorTestCSVSchemaImportCSVFile=An error occured when importing the CSV File: Error '{0}'
    ErrorTestCSVSchemaMissingColumns=Error. Validation Failed: Missing Columns '{0}'
    ErrorTestCSVSchemaDifferentAreaColumn=Formatting Error. Validation Failed: Cannot have different Areas ('{0}') within the same CSV File.
    ErrorTestCSVSchemaDuplicateContributionArea=Duplicate Contribution Area Found with SecondContributionArea and ThirdContributionArea columns within the CSV File. Please correct this issue and try again!
    ErrorParseValueCheck=Parser Check Failed. Please ensure that 'Value' has the correct names added that are appropriate for the respective 'Area'. Please run "Get-AreaNamedValues -AreaName '{0}'" to get correct 'Value' names. Alternatively, you can run: 'New-MVPFixture -AreaName '{0}' to generate an example template.
    ErrorParseContributionAreaCheck=Parser Check Failed. Please ensure that 'ContributionArea' has the correct names added. Please run "Get-Help ContributionArea" for a list of valid Contribution Areas.
    Error500=Error 500 Detected on the MVP Portal. This is an intermittent error.
    WarningEntryWasNotSaved=An Error occured when attempting to add the entry. THE ENTRY WAS NOT SAVED.
    ElementIdActivityType=activityTypeSelector
    ElementIdContributionArea=select_contributionAreasDDL
    ElementButtonNewActivity=addNewActivityBtn
    ElementButtonCancelActivity=submitCloseButton
    ElementButtonSubmitActivity=submitActivityButton
    ElementValueArticle=e36464de-179a-e411-bbc8-6c3be5a82b68
    ElementValueChefPuppetInDataCenter=b803f4ef-066b-e511-810b-fc15b428ced0
    ElementFieldValidationError=field-validation-error
    ElementVisibilityBoxXPath=//a[contains(@class,'Txt') and contains(@role,'navigation')]
    VisibilityListItem=//li[contains(@class,'Item') and contains(@txt,'{0}')]
    VariableSaveActivitySleepCounter=SaveActivitySleepCounter
    TestActivityRegexMVPActivity=^MVPActivity
    TestActivityRegexMVPArea=^Area
    TestActivityRegexMVPContributionArea=^ContributionArea
    TestActivityRegexMVPVisibility=^Visibility
    TestActivityRegexMVPValue=^Value
    ConnectToMVPPortalRegexURLMatch=^https:\/\/login\.((microsoftonline)|(live))\.com
    MVPPortal500Error=^https:\/\/mvp\.microsoft\.com\/Error\/500\?.+
'@

}
#Resource: 04_HTMLContributionAreas.ps1
# Read-Only
New-Variable -Name HTMLContributionAreas -Scope Global -Option ReadOnly -Force -Value @(
'Chef/Puppet in Datacenter'
'Container Management'
'Datacenter Management'
'Group Policy'
'High Availability'
'Hyper-V'
'Linux in System Center/Operations Management Suite'
'Linux on Hyper-V'
'Networking'
'PowerShell'
'Storage'
'Windows Server for Small & Medium Business'
'Azure Bot Service'
'Azure Cognitive Search'
'Azure Cognitive Services'
'Azure Machine Learning'
'Dynamics 365'
'Power Apps'
'Power Automate'
'Power Pages'
'Power Virtual Agents'
'Azure Arc Enabled Data Services'
'Azure Cosmos DB'
'Azure Data Catalog'
'Azure Data Explorer'
'Azure Data Lake'
'Azure Database for MySQL'
'Azure Database for PostgreSQL'
'Azure Databricks'
'Azure HDInsight and Hadoop & Spark on Azure'
'Azure Search'
'Azure SQL (Database, Pools, Serverless, Hyperscale, Managed Instance, Virtual Machines)'
'Azure SQL Edge'
'Azure Stream Analytics'
'Azure Synapse Analytics'
'Big Data Clusters'
'Cortana Intelligence Suite'
'Data Warehousing (Azure SQL Data Warehouse, Fast Track & APS)'
'Information Management (ADF, SSIS, & Data Sync)'
'Microsoft Purview'
'Power BI'
'SQL Server (on Windows, Linux, Containers)'
'SQL Server Machine Learning Services (R, Python)'
'SQL Server Reporting Services & Analysis Services'
'Tools & Connectivity'
'.NET'
'Accessibility'
'ASP.NET/IIS'
'C++'
'Developer Security'
'Front End Web Dev'
'Github & Azure DevOps'
'Java'
'Javascript/Typescript'
'Node.js'
'Python'
'Quantum'
'Unity'
'Visual Studio Code'
'Visual Studio Extensibility'
'Xamarin'
'Information Protection'
'Microsoft Intune'
'Previous Expertise: Directory Services'
'Remote Desktop Services'
'Azure Edge Devices'
'Azure IoT Services & Development'
'Access'
'Excel'
'Exchange'
'Microsoft Stream'
'Microsoft Teams'
'Microsoft Viva'
'Office 365'
'OneDrive'
'OneNote'
'Outlook'
'PowerPoint'
'Project'
'SharePoint'
'Skype for Business'
'Visio'
'Word'
'Yammer'
'Microsoft Graph'
'Microsoft Teams Development'
'Outlook Development'
'SharePoint Development'
'W/X/P Development'
'ARM Templates (Infra as Code)'
'Azure API Management'
'Azure App Service'
'Azure Arc Enabled Infrastructure'
'Azure Backup & Disaster Recovery'
'Azure Blockchain'
'Azure Core Compute (VMSS, Confidential Computing, Platform Deployment)'
'Azure Cost Management'
'Azure Functions'
'Azure Hybrid'
'Azure Kubernetes, Container Instances, Docker'
'Azure Lighthouse'
'Azure Logic Apps'
'Azure Migrate'
'Azure Monitor'
'Azure Networking'
'Azure Policy & Governance'
'Azure SDK (Software Development Kit) and CLIs ( Az CLI, PowerShell, Terraform, Ansible)'
'Azure Service Fabric'
'Azure Storage'
'Distributed App Runtime (Dapr), Open App Model (OAM), Open Service Mesh (OSM)'
'Previous Expertise: Microsoft Azure'
'Service Bus, Event Hubs, Event Grid, Relay'
'D365 Mixed Reality'
'MR Design'
'MR Development'
'Cloud Security'
'Identity & Access'
'SIEM & XDR'
'Surface for IT'
'Windows 365'
'Windows for IT'
'Windows Design'
'Windows Development'
)
#BuildFileName: Get-ActivityTypes.ps1
Function Get-ActivityTypes {
    [CmdletBinding()]
    param()

    Write-Verbose "Get-ActivityType Called:"

    Test-SEDriver

    # If the Activity Types have already been cached, then return the cache
    if (Test-SEActivityTypes) {
        Write-Verbose "[Get-ActivityType] Returning Cache:"
        return $Global:SEActivityTypes
    }

    # Load and Parse the HTML
    $HTMLDoc = [HtmlAgilityPack.HtmlDocument]::new()
    $HTMLDoc.LoadHtml($Global:MVPDriver.PageSource)

    # Retrive the Contribution Areas
    $ActivityTypes = $HTMLDoc.GetElementbyId("activityTypeSelector").ChildNodes.Where{$_.Name -eq 'option'}

    if ($ActivityTypes.count -eq 0) { $PSCmdlet.ThrowTerminatingError($LocalizedData.ErrorMissingActivityType)}

    # Build Custom Object
    $Global:SEActivityTypes = ($ActivityTypes | Select-Object @{
                                        Name="Name"
                                        Expression={$_.InnerText}}, 
                                    @{
                                        Name="Value"
                                        Expression={($_.Attributes.Where{$_.Name -eq 'value'}).Value}
                                    })

    Write-Debug ("[Get-ActivityType] Global:SEActivityTypes: {0}" -f ($Global:SEActivityTypes | ConvertTo-Json))

    Write-Output $Global:SEActivityTypes

}

#BuildFileName: Get-ASTInstanceValues.ps1
function Get-ASTInstanceValues {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [System.Management.Automation.Language.CommandAst[]]
        $InstanceValues,
        [Parameter()]
        [string]
        $ParameterName
    )
 
    $values = [System.Collections.Generic.List[String]]::New()

    # Split Instances with ParameterNames and Without ParameterNames
    $InstanceValuesWithParameterNames,$InstanceValuesWithoutParameterNames = $InstanceValues.Where({
        $_.CommandElements.Where{$_.ParameterName}
    }, 'Split')

    # Return a list of cmdlet's that have parsed parameters into the input and capture their values
    forEach ($valueInstance in $InstanceValuesWithParameterNames) {

        $Result = 0..$valueInstance.CommandElements.count | Where-Object { 
            ($valueInstance.CommandElements[$_].ParameterName -eq $ParameterName) 
        }

        # Get the following item in the array, which will be the value of the parameter name
        $values.Add($valueInstance.CommandElements[$Result+1].Value)

    }

    $InstanceValuesWithoutParameterNames | ForEach-Object { $values.Add( $_.CommandElements[1].Value )  }

    return $values

}
#BuildFileName: Get-ContributionAreas.ps1
Function Get-ContributionAreas {
    [CmdletBinding()]
    param()

    Write-Verbose "Get-ContributionAreas Called:"

    Test-SEDriver

    # If the Activity Types have already been cached, then return the cache
    if (Test-SEContributionAreas) {
        Write-Verbose "[Get-ContributionAreas] Returning Cache:"
        return $Global:SEContributionAreas
    }

    # Load and Parse the HTML
    $HTMLDoc = [HtmlAgilityPack.HtmlDocument]::new()
    $HTMLDoc.LoadHtml($Global:MVPDriver.PageSource)

    # Retrive the Contribution Areas
    $ContributionAreas = $HTMLDoc.GetElementbyId("select_contributionAreasDDL").ChildNodes.Where{$_.Name -eq "optgroup"}

    # Filter by Non-Disabled Attributes. Disabled Attributes are headers
    $OptionElements = $ContributionAreas.ChildNodes.Where{$_.Attributes.Name -notcontains 'disabled'}

    if ($OptionElements.count -eq 0) { $PSCmdlet.ThrowTerminatingError($LocalizedData.ErrorMissingContributionType)}

    # HTML Sanitization
    $htmlSanitizer = {
        param($value)

        $value = $value -replace '&', '&'
        $value = $value -replace ' ', ''

        Write-Output $value.trim()

    }

    # Build Custom Object
    $Global:SEContributionAreas = ($OptionElements | Select-Object @{
                                        Name="Name"
                                        Expression={ $htmlSanitizer.Invoke($_.InnerText) }
                                    }, 
                                    @{
                                        Name="Value"
                                        Expression={($_.Attributes.Where{$_.Name -eq 'data-contributionid'}).Value}
                                    })
    
    Write-Debug ("[Get-ContributionAreas] Global:SEContributionAreas: {0}" -f ($Global:SEActivityTypes | ConvertTo-Json))
    Write-Output $Global:SEContributionAreas

}

#BuildFileName: Get-HTMLFormStructure.ps1
function Get-HTMLFormStructure {
    [CmdletBinding()]
    param (
       # Parameter help description
       [Parameter(Mandatory)]
       [String]
       $Name
    )

    Write-Verbose "Get-HTMLFormStructure Started:"

    # Trim the input so that we can be more forgiving.
    $Name = $Name.Trim()

    $output = [System.Collections.Generic.List[PSCustomObject]]::New()

    # The Default Parameter Value is Prohibited
    if ($Name -eq "Default") { Throw $LocalizedData.ErrorHTMLFormStructureDefaultParameter }

    $defaultProperties = $Global:HTMLFormStructure.Where{$_.Name -eq 'Default'}
    [Array]$matched = $Global:HTMLFormStructure.Where{($_.Name -eq $Name) -or ($_.Element -eq $Name)}

    # If the Either The Element of the String name can't be found,
    # then Throw a terminating error.
    if ($matched.count -eq 0) {
        Throw ($LocalizedData.ErrorHTMLFormStructureMissingName -f $Name)
    }

    # If there is more then 1 result, data duplicate. Throw temrinating error.
    if ($matched.count -ne 1) {
        Throw ($LocalizedData.ErrorHTMLFormStructureMissingName -f $Name)
    }

    # Join the Default data with the matched data
    forEach ($Property in $defaultProperties.Properties) {
        # Test if the Element Already Exists. If so, skip.
        if ($matched.Properties.Element -contains $Property.Element) { 
            Write-Debug ("[Get-HTMLFormStructure] Duplicate Element '{0}'" -f $Property.Element)
            continue 
        }

        $obj = [PSCustomObject]@{
            Name = $Property.Name
            Element = $Property.Element
            isRequired = $(
                if ($Property.Keys -notcontains "isRequired") { $false }
                else { $Property.isRequired }
            )
            isSet = $false
        }

        Write-Debug ("[Get-HTMLFormStructure] Adding Default Element '{0}' " -f ($obj | ConvertTo-Json))
        $output.Add($obj)     
    }

    forEach ($Property in $matched.properties) {

        $obj = [PSCustomObject]@{
                    Name = $Property.Name
                    Element = $Property.Element
                    isRequired = $(
                        if ($Property.Keys -notcontains "isRequired") { $false }
                        else { $Property.isRequired }
                    )
                    isSet = $false
                }

        Write-Debug ("[Get-HTMLFormStructure] Adding Element '{0}' " -f ($obj | ConvertTo-Json))
        $output.Add($obj)

    }

    #
    # Join the Formatting Scriptblock form the HTMLFormStructureValidate Variable.
    $output = $output | Select-Object *, @{
        Name = "Format"
        Expression = { 
            $Name = $_.Name
            try { ($Global:HTMLFormStructureValidate.Where{$_.Name -eq $Name}).Format } catch {}
        }
    }

    Write-Output $output      
    Write-Verbose "Get-HTMLFormStructure Completed:"

}
#BuildFileName: Invoke-MVPActivity.ps1
function Invoke-MVPActivity {
    [CmdletBinding()]
    param()

    # Update Streams
    Write-Verbose "Invoke-MVPActivity:"

    # Update Debug Stream
    Write-Debug "[Invoke-MVPActivity] Calling Test-SEDriver:"

    # Test if the Driver is active. If not throw a terminating error.
    Test-SEDriver

    # Update Debug Stream
    Write-Debug "[Invoke-MVPActivity] Calling Try-TentativeCommand:"
    Write-Debug ("[Invoke-MVPActivity] Try-TentativeCommand Params: {0}" -f ($params | ConvertTo-Json))

    $result = ttry {

        # Try and click "Add New Activity"

        $ActivityButton = Find-SeElement -Driver $Global:MVPDriver -Id $LocalizedData.ElementButtonNewActivity
        Invoke-SeClick -Element $ActivityButton

        # Update Debug
        Write-Debug "[Invoke-MVPActivity:] Tentative-Try: Success"

        Write-Output $true

    } -catch {

        # Update Debug
        Write-Debug ("[Invoke-MVPActivity] Tentative-Try: Error Raised {0}" -f $_.Exception)
        # Try and close the activity if the window is already open.
        Stop-MVPActivity
        # Sleep for a second
        Start-Sleep -Seconds 1

    } -RetryLimit 4

    if ($null -eq $result) {
        # Update Verbose Stream
        Write-Verbose "Invoke-MVPActivity: Failure"

        Throw $LocalizedData.ErrorNoActivityButton
    }

    # Update Verbose Stream
    Write-Verbose "Invoke-MVPActivity: Success"

    Start-Sleep -Seconds 1

}
#BuildFileName: New-CSVArguments.ps1
function New-CSVArguments {
    param(
        [Parameter(Mandatory)]
        [String]
        $LiteralPath
    )

    # Import the CSV File
    $CSV = Import-Csv @PSBoundParameters

    # Get the CSV Column Names, excluding 'Area' & ContributionArea
    $CSVColumnNames = ($CSV | Get-Member -MemberType NoteProperty).Name

    # Get the HTMLForm Structure and Get the Column Names Match from the CSV File
    $FormStructure = Get-HTMLFormStructure @($CSV.Area)[0] | Where-Object { $_.Name -in $CSVColumnNames }

    $ArgumentList = $CSV | ForEach-Object {

        $row = $_

        $params = @{
            Area = $row.Area
            ContributionArea = ($row.ContributionArea, $row.SecondContributionArea, $row.ThirdContributionArea | Where-Object {$null -ne $_})
        }

        if ($CSVColumnNames -contains 'Visibility') {
            $params.Visibility = $row.Visibility
        }

        # Iterate Through the Matched Items and Add them
        $FormStructure | ForEach-Object {
            $params.('{0}' -f $_.Name) = $row."$($_.Name)"
        }

        Write-Output $params

    }

    Write-Output $ArgumentList
    
}
#BuildFileName: New-CSVFixture.ps1
Function New-CSVFixture {
    param(
        [Parameter(Mandatory)]
        [String]
        $LiteralPath
    )

    # Import the CSV File
    [Array]$CSV = Import-Csv @PSBoundParameters

    # Single CSV Entry
    if ($CSV.Count -eq 1) {
        $Area = $CSV.Area
    } else {
        $Area = $CSV.Area[0]
    }

    # Get the CSV Column Names, excluding 'Area' & ContributionArea
    $CSVColumnNames = ($CSV | Get-Member -MemberType NoteProperty).Name

    # Get the HTMLForm Structure and Get the Column Names Match from the CSV File
    $FormStructure = Get-HTMLFormStructure $Area | Where-Object { $_.Name -in $CSVColumnNames }

    # If the Visability is present, manually interpolate.
    $VisabilityParameter = $(
        if ($CSVColumnNames -contains 'Visibility') { Write-Output ',$Visibility'}
        else { Write-Output '' }
    )

    # Param Block
    $ParamBlock = 'param($Area,$ContributionArea,{0}{1});' -f (($FormStructure.Name | ForEach-Object { "`${$_}" }) -join ','), $VisabilityParameter

    # Create the Scriptblock
    $ScriptBlock = $FormStructure | ForEach-Object -Begin {
        $ParamBlock
    } -Process {
        "Value '{0}' {1}" -f $_.Name, "`${$($_.Name)};"
    }

    # Return our Fixture Goodness
    return ([scriptblock]::Create($ScriptBlock))

}
#BuildFileName: New-MVPActivity.ps1
function New-MVPActivity {
    [CmdletBinding(DefaultParameterSetName="Default")]
    param (
        # Scriptblock of the Activity
        [Parameter(Mandatory,Position=1,ParameterSetName="Arguments")]
        [Parameter(Mandatory,Position=1,ParameterSetName="Default")]
        [ScriptBlock]
        $Fixture,
        # ArgumentList of the Activity
        [Parameter(Position=2,ParameterSetName="Arguments")]
        [HashTable]
        $ArgumentList      
    )
    
    begin {

        Write-Debug "[New-MVPActivity] BEGIN: Buildup of Activity"

        #
        # Activity Setup

        $invokeTearDown = $false
        $isPreParse = $true
        # Setup of Contributions and Areas
        $Script:MVPArea = $null
        $Script:ContributionAreas = [System.Collections.Generic.List[PSCustomObject]]::New()
        $Script:MVPHTMLFormStructure = $null

        try {

            #
            # Test the configuration is valid.
            $parameterCmdlets = Test-ActivityScriptBlock @PSBoundParameters
            # Test that the driver is active
            Test-SEDriver

            $isPreParse = $false

            # Test that the MVPActivity elements is present.
            Wait-ForMVPElement
            # Create new MVPActivity. Click the Button.
            Invoke-MVPActivity    
             
        } catch {
            Write-Debug "[MVPActivity] Failed MVP Validation Error Below:"
            Write-Error $_
            $invokeTearDown = $true
        }

        Write-Debug "[MVPActivity] BEGIN: Buildup Complete"
   
    }
    
    process {
        
        if ($invokeTearDown) { return }

        Write-Debug "[New-MVPActivity] PROCESS:"

        # Invoke the Fixture

        try {

            # If the Parameter is used instead of the cmdlet itself, invoke the cmdlet parsing in the parametervalue
            if ($parameterCmdlets.ParametrizedArea) {
                Area $ArgumentList.Area
            }
            
            if ($parameterCmdlets.ParametrizedContributionArea) {
                ContributionArea $ArgumentList.ContributionArea
            }

            if ($parameterCmdlets.ParametrizedVisibility) {
                Visibility $ArgumentList.Visibility
            }

            if ($ArgumentList) {
                # Invoke the Fixture Splatting the Args in as parameters.
                & $Fixture @ArgumentList
            } else {
                $null = $Fixture.Invoke()
            }
            # Save the MVP Activity
            Save-MVPActivity
            
        } catch {
            Write-Error $_
            Write-Warning $LocalizedData.WarningEntryWasNotSaved
            Write-Debug "Error was triggered, initiate the teardown of the MVPActivity"
            $invokeTearDown = $true
        }
        
    }
    end {

        Write-Debug "[New-MVPActivity] END: Beginning Teardown"

        #
        # Activity Tear Down
        #

        try {

            if ($invokeTearDown -and (-not $isPreParse)) {
                # Close the MVP Activity
                try {
                    Stop-MVPActivity
                } catch {
                    Write-Warning $_
                }
            }

        } finally {
            # TearDown of Contributions and Areas
            $Script:MVPArea = $null
            $Script:ContributionAreas = [System.Collections.Generic.List[PSCustomObject]]::New()
            $Script:MVPHTMLFormStructure = $null               
        }

        Write-Debug "[New-MVPActivity] END: Completed"

        #
        # Wait for the Window to be closed
        #

        Wait-ForActivityWindow

        #
        # Return to the Caller
        #

        return $null
        
    }

}
#BuildFileName: Save-MVPActivity.ps1
function Save-MVPActivity {
    [cmdletbinding()]
    param()

    # Test if the Driver is active. If not throw a terminating error.
    Test-SEDriver
    
    # Validate that HTML Form Strucuture Prior to Submission
    # Ensure that all required items are set.
    $missingRequiredEntries = $Script:MVPHTMLFormStructure.Where{($_.isSet -eq $false) -and ($_.isRequired -eq $true)}
    if ($missingRequiredEntries) {
        Throw ($LocalizedData.ErrorMissingRequiredEntries -f (-join $missingRequiredEntries.Name))
        # Fail the Submission and Tidy Up
    }
    
    #
    # Search for Field Validation Errors

    $fieldValidationErrors = Find-SeElement -Target $Global:MVPDriver -By ClassName -Selection $LocalizedData.ElementFieldValidationError
    if ($fieldValidationErrors) {
        Throw ($LocalizedData.ErrorFieldValidationError -f $fieldValidationErrors.Text)
    }

    #
    # Save the Activity

    $GetDriverParams = @{
        Driver = $Global:MVPDriver
        Id = $LocalizedData.ElementButtonSubmitActivity
    }

    try {
        # Click the Save Button
        $SaveButton = Find-SeElement -Driver $Global:MVPDriver -Id $LocalizedData.ElementButtonSubmitActivity
        Invoke-SeClick -Element $SaveButton
        # Look for error 500's. The URL will approx redirect to:
        # https://mvp.microsoft.com/Error/500?aspxerrorpath=/
        # There are MSFT Issues. Stop
        if ($Global:MVPDriver.Url -match $LocalizedData.MVPPortal500Error) { Throw $LocalizedData.Error500 }
    } catch {
        Throw ($LocalizedData.ErrorSavingMVPActivity -f $_)
    }
    
}
#BuildFileName: Select-DropDown.ps1
function Select-DropDown {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $elementId,
        [Parameter(Mandatory)]
        [string]
        $selectedValue
    )

    try {
        $ActivityType = Find-SeElement -Driver $Global:MVPDriver -Id $elementId
        $SelectElement = New-Object -TypeName OpenQA.Selenium.Support.UI.SelectElement $ActivityType
        $SelectElement.SelectByValue($selectedValue)
    } catch {
        $PSCmdlet.ThrowTerminatingError($LocalizedData.ErrorSelectingDropDown -f $elementId, $selectedValue, $_)
    }

}
#BuildFileName: Stop-MVPActivity.ps1
function Stop-MVPActivity {
    [CmdletBinding()]
    param (
    )

    # Test if the Driver is active. If not throw a terminating error.
    Test-SEDriver
    
    try {
        $CancelButton = Find-SeElement -Driver $Global:MVPDriver -Id $LocalizedData.ElementButtonCancelActivity
        Invoke-SeClick -Element $CancelButton 
    } catch {
        Throw ($LocalizedData.ErrorStopMVPActivity -f $_)
    }
       
}
#BuildFileName: Test-ActivityScriptBlock.ps1
function Test-ActivityScriptBlock {
    [CmdletBinding()]
    param (
        # Scriptblock to invoke
        [Parameter(Mandatory)]
        [ScriptBlock]
        $Fixture,
        [Parameter()]
        [HashTable]
        $ArgumentList
    )

    Write-Debug "[Test-ActivityScriptBlock] Validating Scriptblock:"

    #
    # Return an Object
    $resultObject = [PSCustomObject]@{
        ParametrizedArea = $false
        ParametrizedContributionArea = $false
        ParametrizedVisibility = $false
    }

    #
    # Return a list of Commands
    $CommandAst = $Fixture.ast.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
    $AreaValue = $null
    $ContributionAreaValues = $null

    # Requirement 1:
    # Ensure that there are no additional MVPActivity scriptblocks within it.
    if ($CommandAst -match $LocalizedData.TestActivityRegexMVPActivity) {
        Throw $LocalizedData.ErrorNestedMVPActivity
    }

    # Requirement 2:
    # Ensure that a Area is present and it's a single instance.
    $areaInstance = $CommandAst -match $LocalizedData.TestActivityRegexMVPArea
    if (($areaInstance.count -eq 0) -and ($null -eq $ArgumentList.Area)) {
        # No Instances were found
        Throw $LocalizedData.ErrorMissingMVPActivityArea
    } elseif (($areaInstance.count -eq 0) -and ($null -ne $ArgumentList.Area)) {
        $resultObject.ParametrizedArea = $true
        $AreaValue = $ArgumentList.Area
    } elseif ($areaInstance.count -ne 1 ) {
        # Multiple Statements of Area was defined
        Throw $LocalizedData.ErrorMissingMVPActivityAreaMultiple
    } elseif (($areaInstance.count -ne 0) -and ($null -eq $ArgumentList.Area)) {
        $AreaValue = Get-ASTInstanceValues -InstanceValues $areaInstance -ParameterName 'Name'
    }

    # Requirement 3:
    # Ensure that the ContributionArea is present and that there is a maximum of two contributions.
    $ContributionAreaInstance = $CommandAst -match $LocalizedData.TestActivityRegexMVPContributionArea
    if ((($ContributionAreaInstance).Count -eq 0) -and ($null -eq $ArgumentList.ContributionArea)) {
        Throw $LocalizedData.ErrorMissingMVPActivityContributionArea
    } elseif (($ContributionAreaInstance.count -eq 0) -and ($null -ne $ArgumentList.ContributionArea)) {
        $resultObject.ParametrizedContributionArea = $true
        $ContributionAreaValues = $ArgumentList.ContributionArea        
    } elseif (($ContributionAreaInstance).Count -gt 3)  {
        Throw ($LocalizedData.ErrorExceedMVPActivityContributionArea -f $ContributionAreaInstance.Count)
    } elseif (($ContributionAreaInstance.count -ne 0) -and ($null -eq $ArgumentList.ContributionArea)) {
        $ContributionAreaValues = Get-ASTInstanceValues -InstanceValues $ContributionAreaInstance -ParameterName 'SelectedValue'
    }

    # Requirement 4:
    # Ensure that the Value Cmdlet is present.
    [Array]$valueInstances = $CommandAst -match $LocalizedData.TestActivityRegexMVPValue
    if ($valueInstances.Count -eq 0) {
        Throw $LocalizedData.ErrorMissingMVPActivityValue
    }

    # Requirement 5:
    # Visibility dosen't need to be included into the statement, however ensure that there are only one of them.
    [Array]$visibilityInstances = $CommandAst -match $LocalizedData.TestActivityRegexMVPVisibility
    if ($visibilityInstances.Count -gt 1) {
        Throw $LocalizedData.ErrorMultipleVisibilityStatements
    } 
    # If Visability is included as a parameter, process it.
    elseif (($visibilityInstances.count -eq 0) -and ($null -ne $ArgumentList.Visibility)) {
        $resultObject.ParametrizedVisibility = $true          
    }

    # Requirement 6:
    # Ensure that all Values added to the fixture are (required) AND are correct
    $HTMLFormStructure = Get-HTMLFormStructure -Name $AreaValue
    $ASTInstanceValues = Get-ASTInstanceValues -InstanceValues $valueInstances -ParameterName 'Name'

    $testMandatoryValues = $HTMLFormStructure | Where-Object { (
        ($_.isRequired) -and 
        (
            ($_.Name -notin $ASTInstanceValues) -and 
            ($_.Element -notin $ASTInstanceValues)
        )    
    )}

    $testMisnamedValues = $ASTInstanceValues | Where-Object {
        $_ -notin $HTMLFormStructure.Name -and 
        $_ -notin $HTMLFormStructure.Element
    }

    if ($testMandatoryValues -or $testMisnamedValues) {
        Throw ($LocalizedData.ErrorParseValueCheck -f $AreaValue)
    }

    # Requirement 5:
    # Ensure that all ContributionAreas are added to the fixture are (required) AND are correct

    if ($ContributionAreaValues | Where-Object { $_ -notin $Global:HTMLContributionAreas}) {
        Throw ($LocalizedData.ErrorParseContributionAreaCheck -f $AreaValue)
    }

    #
    # Finished

    Write-Output $resultObject
    
    Write-Debug "[Test-ActivityScriptBlock] All Tests Passed:"

}
#BuildFileName: Test-CallStack.ps1
function Test-CallStack {
    [CmdletBinding()]
    param(
        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $Name
    )

    if ((Get-PSCallStack).Command -notcontains "MVPActivity") {
        Throw ($LocalizedData.ErrorMissingMVPActivityInCallStack -f $Name)
    }

}
#BuildFileName: Test-CSVSchema.ps1
Function Test-CSVSchema {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory)]
        [String]
        $LiteralPath
    )

    $RequiredTopLevelColumns = @('Area','ContributionArea')

    # Test-Path
    if (-not(Test-Path @PSBoundParameters -ErrorAction SilentlyContinue)) {
        Throw ($LocalizedData.ErrorTestCSVSchemaMissingCSVFile -f $LiteralPath)
    }

    try {
        # Import the CSV File
        $CSV = Import-Csv @PSBoundParameters
    } catch {
        Throw ($LocalizedData.ErrorTestCSVSchemaImportCSVFile -f $_)
    }

    # Get the CSV Column Names
    $CSVColumnNames = ($CSV | Get-Member).Name

    # Validate if the Area/ ContributionArea Columns are present.
    $MissingTopLevelColumns = $RequiredTopLevelColumns | Where-Object {
        $CSVColumnNames -notcontains $_
    }
    # If items were matched then throw a terminating error.
    if ($MissingTopLevelColumns.Count -ne 0) {
        Throw ($LocalizedData.ErrorTestCSVSchemaMissingColumns -f ($MissingTopLevelColumns -join ' , '))
    }

    # Ensure that the Areas are all the same. Group by the Property Area
    $GroupedItems = $CSV | Group-Object -Property Area

    # Retrive the Area and Contribution Areas
    if ($GroupedItems.Length -ne 1) {
        Throw ($LocalizedData.ErrorTestCSVSchemaDifferentAreaColumn -f $GroupedItems.Name -join ' , ')
    }

    $Area = $GroupedItems | Select-Object -First 1 -ExpandProperty Name

    # Perform a Lookup to workout what columns we need
    $FormStructure = Get-HTMLFormStructure $Area | Where-Object {$_.isRequired}

    # Validate that the required Name or Element is Present within the CSV File
    $MissingCSVColumns = $FormStructure | Where-Object { $_.Name -notin $CSVColumnNames }
    if ($MissingCSVColumns.Count -ne 0) {
        Throw ($LocalizedData.ErrorTestCSVSchemaMissingColumns -f $MissingCSVColumns.Name -join ' , ')
    }    

    # Validate no duplicate Contribution Area's are present

    ForEach($Row in $CSV) {

        if ((-not([String]::IsNullOrEmpty($Row.SecondContributionArea))) -or (-not($Row.ThirdContributionArea))) {
            $arr = $Row.ContributionArea, $Row.SecondContributionArea, $Row.ThirdContributionArea | Group-Object | Where-Object {$_.Count -ne 1}
            
            if ($arr.Count -ne 0) {
                Throw ($LocalizedData.ErrorTestCSVSchemaDuplicateContributionArea)
            }
            
        }

    }

}
#BuildFileName: Test-MVPDriverisMicrosoftLogin.ps1
function Test-MVPDriverisMicrosoftLogin {
    [CmdletBinding()]
    param (
        [Parameter()]
        [Switch]
        $waitUntilLoaded,
        [Parameter()]
        [Switch]
        $isCompleted
    )
    # The parameters provide pester guidance on how to mock this function
    Write-Output ($Global:MVPDriver.Url -match $LocalizedData.ConnectToMVPPortalRegexURLMatch)
}
#BuildFileName: Test-SEActivityTypes.ps1
function Test-SEActivityTypes {
    
    $result = $false
    # If there Driver Variable is $null. Throw a Terminating Error
    if ($null -ne $Global:SEActivityTypes) { $result = $true }
    Write-Output $result

}
#BuildFileName: Test-SEContributionAreas.ps1
function Test-SEContributionAreas {
    [cmdletbinding()]
    param()
    $result = $false
    # If there Driver Variable is $null. Throw a Terminating Error
    if ($null -ne $Global:SEContributionAreas) { $result = $true }
    Write-Output $result

}
#BuildFileName: Test-SEDriver.ps1
function Test-SEDriver {
    [CmdletBinding()]
    param()
    # If there Driver Variable is $null. Throw a Terminating Error
    if ($null -eq $Global:MVPDriver) {
        Throw ($LocalizedData.ErrorMissingDriver) 
    } elseif ($null -eq $Global:MVPDriver.URL) {
        Throw ($LocalizedData.ErrorMissingDriver) 
    }

}
#BuildFileName: Try-TentitiveCommand.ps1
function ttry {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, Position = 0)]
        [Scriptblock]
        $Try,
        [Parameter(Mandatory,  Position = 1)]
        [Scriptblock]
        $Catch,
        [Parameter(Position = 3)]
        [Int]
        $RetryLimit = 3
    )

    $Count = 0
    Do {

        Try {
            $Result = $Try.Invoke()
            break
        } Catch {            
            $Result = $Catch.Invoke()
            $Count++
        }

    } Until ($Count -eq $RetryLimit)

    if ($Count -eq $RetryLimit) {
        Write-Error $LocalizedData.ErrorTryTentitiveCommand
    }

    Write-Debug "[Try-TentativeCommand] Returning:"
    
    return $Result

}

#Yup it's gotta be flipped, because I need to mock ttry and I can't mock and alias.
Set-Alias -Name "Try-TentativeCommand" -Value "ttry"
#BuildFileName: Wait-ForActivityWindow.ps1
function Wait-ForActivityWindow {
    [CmdletBinding()]
    param (
    )

    # Test the Selenium Driver
    Test-SEDriver

    # Start Verbose
    Write-Verbose "[Wait-ForActivityWindow] Started:"

    # Wait for the MVPActivity Window to be closed.
    try {
        $DialogElement = Find-SeElement -Driver ($Global:MVPDriver) -ClassName 'mvp-dialog'
    } Catch {
        Throw $_
    }

    While (($null -ne $DialogElement) -and ($DialogElement.Displayed)) {
        Write-Verbose "[Wait-ForActivityWindow] Waiting for 'Add a new activity' to close:"
        Start-Sleep -Seconds 1
    }

    # Start Verbose
    Write-Verbose "[Wait-ForActivityWindow] Finished:"

}
#BuildFileName: Wait-ForJavascript.ps1
Function Wait-ForJavascript {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]
        $ElementTextToSearch       
    )
    
    Test-SEDriver

    #
    # Sometimes the Javascript dosen't load and add elements
    # to the HTML page. This checks to see if the element text is present
    # and if so, then continue.

    $retry = 0

    Do {
        if (($Global:MVPDriver).PageSource -like ("*{0}*" -f $ElementTextToSearch)) { break }
        Start-Sleep -Seconds 1
        $retry++
    } Until ($retry -eq 3)
    
    if ($retry -eq 3) {
        Throw $LocalizedData.ErrorJavaScriptTimeout
    }
    
}
#BuildFileName: Wait-ForMVPElement.ps1
function Wait-ForMVPElement {
    [CmdletBinding()]
    param (    
    )

    Write-Verbose "[Wait-ForMVPElement] Started:"

    ttry {
        $ActivityButton = Find-SeElement -Driver $Global:MVPDriver -Id $LocalizedData.ElementButtonNewActivity
        if ($null -eq $ActivityButton) {
            Enter-SeUrl 'https://mvp.microsoft.com/en-us/MyProfile/EditActivity' -Driver $Global:MVPDriver             
            Throw "Missing"
        }
    } -catch {
        # Look for error 500's. The URL will approx redirect to:
        # https://mvp.microsoft.com/Error/500?aspxerrorpath=/
        # There are MSFT Issues. Stop
        if ($Global:MVPDriver.Url -match $LocalizedData.MVPPortal500Error) { Throw $LocalizedData.Error500 }

        Write-Debug ("Waiting for New Activity Button. {0}" -f $LocalizedData.ElementButtonNewActivity)
        Write-Warning "Waiting for New Activity Button."        
    } -RetryLimit 10

    Write-Verbose "[Wait-ForMVPElement] Completed:"
    
}
#BuildFileName: Area.ps1
function Area {
<#
.Description
Used inside of the MVPActivity block, the ‘Area’ Describes the Contribution Area.
The Current List of Areas are:

+ Article
+ Blog/WebSite Post
+ Book (Author)
+ Book (Co-Author)
+ Conference (Staffing)
+ Docs.Microsoft.com Contribution
+ Forum Moderator
+ Forum Participation (3rd Party forums)
+ Forum Participation (Microsoft Forums)
+ Mentorship
+ Microsoft Open Source Projects
+ Non-Microsoft Open Source Projects
+ Organizer (User Group/Meetup/Local Events)
+ Organizer of Conference
+ Other
+ Product Group Feedback
+ Sample Code/Projects/Tools
+ Site Owner
+ Speaking (Conference)
+ Speaking (User Group/Meetup/Local events)
+ Technical Social Media (Twitter, Facebook, LinkedIn...)
+ Translation Review, Feedback and Editing
+ Video/Webcast/Podcast
+ Workshop/Volunteer/Proctor

.PARAMETER SelectedValue
The MVP Contribution Area

.EXAMPLE
Area 'Article'

.SYNOPSIS
'Area' defines the MVP Contribution Area within the Portal. Area can only be used within MVPActivity.
#>

    [CmdletBinding()]
    param(
        # Selected Value
        [Parameter(Mandatory=$true)]
        [String]
        $SelectedValue
    )
   
    begin {

        # Test if the Driver is active. If not throw a terminating error.
        Test-SEDriver
        # Test the Callstack.
        Test-CallStack $PSCmdlet.MyInvocation.MyCommand.Name
        
    }

    Process {


        # Validate the $SelectedValue Attributes
        [Array]$matchedActivityType = Get-ActivityTypes | Where-Object { $_.Name -eq $SelectedValue }
        if ($matchedActivityType.Count -eq 0) { Throw ($LocalizedData.ErrorMissingSelectedValue -f $SelectedValue) }
        if ($matchedActivityType.Count -ne 1) { Throw ($LocalizedData.ErrorTooManySelectedValue -f $SelectedValue, $matchedActivityType.count) }
        
        # If the Activity Type matches, we need to do a lookup to get the HTML Form structure
        $HTMLFormStructure = Get-HTMLFormStructure $SelectedValue

        # Select the Element
        $output = ttry {

            Select-DropDown -elementId $LocalizedData.ElementIdActivityType -selectedValue $matchedActivityType.Value

            # Iterate through the HTML Structure and validate if the fields exist.
            $HTMLFormStructure | ForEach-Object {
                $Element = Find-SeElement -Driver ($Global:MVPDriver) -Id $_.Element -Timeout 1
                if (-not($Element)) {
                    Throw $LocalizedData.ErrorJavaScriptTimeout
                }
            }

            # Update the Area
            $Script:MVPArea = $SelectedValue
            $Script:MVPHTMLFormStructure = $HTMLFormStructure

            Write-Output $true

        } -Catch {       
            # If the Javascript Fails to Populate the Sub entries within the form
            # it will retrigger by select the "Article"
            Start-Sleep -Seconds 1
            Select-DropDown -elementId $LocalizedData.ElementIdActivityType -selectedValue $LocalizedData.ElementValueArticle   
        } -RetryLimit 5
        
        # If it failed to select the Area, we need to fail the cmdlet
        if ($output -ne $true) {
            # Throw a terminating error
            Throw $LocalizedData.ErrorAreaFailure
        }

    }

}
#BuildFileName: ConnectTo-MVPPortal.ps1
function ConnectTo-MVPPortal {
<#
.Description
Connects to the MVP Portal and waits for the login to complete.

.PARAMETER URLPath
A custom URL provided in the MVP email. Otherwise, if excluded, it will prompt the user for a URL.

.PARAMETER DriverType
You can specify different browser types. The default is Firefox. Options are: Firefox, Chrome, Edge, OldEdge

.EXAMPLE
Connect to Portal:

ConnectTo-MVPPortal -URLPath "https://Your-URL-Path-Goes-Here.com"
.EXAMPLE
Connect to Portal:

ConnectTo-MVPPortal -URLPath "https://Your-URL-Path-Goes-Here.com" -DriverType ('Firefox','Chrome' or 'Edge')
.SYNOPSIS
Connects to the MVP Portal and waits for the login to complete.
#>
    
    [CmdletBinding()]
    param (
        [Parameter()]
        [String]
        $URLPath=(Read-Host "Enter in URL Path"),
        [ValidateSet("Firefox","Chrome","Edge", "OldEdge")]
        [String]
        $DriverType = 'Firefox'
    )

    try {

        switch ($DriverType) {
            'Firefox' { $Global:MVPDriver = Start-SeFirefox -StartURL $URLPath }
            'Chrome'  { $Global:MVPDriver = Start-SeChrome  -StartURL $URLPath }
            'Edge'    { $Global:MVPDriver = Start-SeNewEdge -StartURL $URLPath }
            'OldEdge' { $Global:MVPDriver = Start-SeEdge -StartURL $URLPath    }
        }
                
    } catch {
        $Global:MVPDriver = $null
        Throw ($LocalizedData.ErrorConnectToMVPPortal -f $_)       
    }

    # Wait until the Login Screen Loads
    do {
        Test-SEDriver
        Write-Verbose "Waiting for the URL to redirect to the Microsoft Login"
        Start-Sleep -Seconds 1
    } Until (Test-MVPDriverisMicrosoftLogin -waitUntilLoaded)

    # Then Pause Execution until login process has completed
    while (Test-MVPDriverisMicrosoftLogin -isCompleted) {
        Test-SEDriver
        Write-Verbose "Waiting for Login to complete:"
        Start-Sleep -Seconds 1
    }

    Write-Verbose "Success! Logged in."
}
#BuildFileName: ContributionArea.ps1
Function ContributionArea {
<#
.Description
The ContributionArea defines the Contribution Areas within a contribution.
ContributionArea can be used as a String Array (ContributionArea 'Area1','Area2','Area3'), or by specifying multiple ContributionArea statements.

Similarly to Area, if ContributionArea is included within the Param() block, it's not required to be defined and will be automatically invoked.

The current list of 'Contribution Areas' are:
+ Chef/Puppet in Datacenter
+ Container Management
+ Datacenter Management
+ Group Policy
+ High Availability
+ Hyper-V
+ Linux in System Center/Operations Management Suite
+ Linux on Hyper-V
+ Networking
+ PowerShell
+ Storage
+ Windows Server for Small & Medium Business
+ Azure Bot Service
+ Azure Cognitive Search
+ Azure Cognitive Services
+ Azure Machine Learning
+ Dynamics 365
+ Power Apps
+ Power Automate
+ Power Pages
+ Power Virtual Agents
+ Azure Arc Enabled Data Services
+ Azure Cosmos DB
+ Azure Data Catalog
+ Azure Data Explorer
+ Azure Data Lake
+ Azure Database for MySQL
+ Azure Database for PostgreSQL
+ Azure Databricks
+ Azure HDInsight and Hadoop & Spark on Azure
+ Azure Search
+ Azure SQL (Database, Pools, Serverless, Hyperscale, Managed Instance, Virtual Machines)
+ Azure SQL Edge
+ Azure Stream Analytics
+ Azure Synapse Analytics
+ Big Data Clusters
+ Cortana Intelligence Suite
+ Data Warehousing (Azure SQL Data Warehouse, Fast Track & APS)
+ Information Management (ADF, SSIS, & Data Sync)
+ Microsoft Purview
+ Power BI
+ SQL Server (on Windows, Linux, Containers)
+ SQL Server Machine Learning Services (R, Python)
+ SQL Server Reporting Services & Analysis Services
+ Tools & Connectivity
+ .NET
+ Accessibility
+ ASP.NET/IIS
+ C++
+ Developer Security
+ Front End Web Dev
+ Github & Azure DevOps
+ Java
+ Javascript/Typescript
+ Node.js
+ Python
+ Quantum
+ Unity
+ Visual Studio Code
+ Visual Studio Extensibility
+ Xamarin
+ Information Protection
+ Microsoft Intune
+ Previous Expertise: Directory Services
+ Remote Desktop Services
+ Azure Edge Devices
+ Azure IoT Services & Development
+ Access
+ Excel
+ Exchange
+ Microsoft Stream
+ Microsoft Teams
+ Microsoft Viva
+ Office 365
+ OneDrive
+ OneNote
+ Outlook
+ PowerPoint
+ Project
+ SharePoint
+ Skype for Business
+ Visio
+ Word
+ Yammer
+ Microsoft Graph
+ Microsoft Teams Development
+ Outlook Development
+ SharePoint Development
+ W/X/P Development
+ ARM Templates (Infra as Code)
+ Azure API Management
+ Azure App Service
+ Azure Arc Enabled Infrastructure
+ Azure Backup & Disaster Recovery
+ Azure Blockchain
+ Azure Core Compute (VMSS, Confidential Computing, Platform Deployment)
+ Azure Cost Management
+ Azure Functions
+ Azure Hybrid
+ Azure Kubernetes, Container Instances, Docker
+ Azure Lighthouse
+ Azure Logic Apps
+ Azure Migrate
+ Azure Monitor
+ Azure Networking
+ Azure Policy & Governance
+ Azure SDK (Software Development Kit) and CLIs ( Az CLI, PowerShell, Terraform, Ansible)
+ Azure Service Fabric
+ Azure Storage
+ Distributed App Runtime (Dapr), Open App Model (OAM), Open Service Mesh (OSM)
+ Previous Expertise: Microsoft Azure
+ Service Bus, Event Hubs, Event Grid, Relay
+ D365 Mixed Reality
+ MR Design
+ MR Development
+ Cloud Security
+ Identity & Access
+ SIEM & XDR
+ Surface for IT
+ Windows 365
+ Windows for IT
+ Windows Design
+ Windows Development

.PARAMETER SelectedValue
The Contribution Areas that you wish to add.

.EXAMPLE
Single Use:

ContributionArea 'Area1'
.EXAMPLE
ContributionArea can be stacked like so:

ContributionArea 'Area1'
ContributionArea 'Area2'
ContributionArea 'Area3'

.EXAMPLE
ContributionArea can placed in an array:

ContributionArea 'Area1','Area2','Area3'

.SYNOPSIS
The ContributionArea is a command that defines the Contribution Areas within the MVP Portal.

#>

    [cmdletbinding()]
    param(
        # Parameter help description
        [Parameter(Mandatory)]
        [String[]]
        $SelectedValue
    )

    begin {

        # Test if the Driver is active. If not throw a terminating error.
        Test-SEDriver
        # Test the Callstack.
        Test-CallStack $PSCmdlet.MyInvocation.MyCommand.Name
        
    }

    process {

        # Multiple Contribution Areas can be parsed in.
        ForEach ($Param in $SelectedValue) {

            # Fetch the Contribution Areas (sometimes the browser is too quick)
            $HTMLContributionAreas = ttry {
                Get-ContributionAreas
            } -Catch { 
                Start-Sleep -Milliseconds 500
            } -RetryLimit 5

            # Validate the $SelectedValue Parameters
            [Array]$matchedActivityType = $HTMLContributionAreas | Where-Object { $_.Name -eq $Param }
            if ($matchedActivityType.Count -eq 0) { Throw ($LocalizedData.ErrorMissingSelectedValue -f $Param) }
            if ($matchedActivityType.Count -ne 1) { Throw ($LocalizedData.ErrorTooManySelectedValue -f $Param, $matchedActivityType.count) }
            
            # Generate the ElementId
            $elementId = $(
                if ($Script:ContributionAreas.Count -eq 0) { $LocalizedData.ElementIdContributionArea }
                else { "{0}{1}" -f $LocalizedData.ElementIdContributionArea, ($Script:ContributionAreas.Count + 1) }
            )

            # Add to the Contribution Areas
            $Script:ContributionAreas.Add(
                [PSCustomObject]@{
                    elementId = $matchedActivityType.Name
                    selectedValue = $matchedActivityType.Value
                }
            )

            #
            # If the $Script:ContributionAreas count is gt then 3, then select the 'add more' link

            if ($Script:ContributionAreas.count -ge 3) {
                $AddMoreButton = Find-SeElement -Driver $Global:MVPDriver -By ClassName -Selection 'add' 
                Invoke-SeClick -Element $AddMoreButton 
            }

            #
            # You will need to inspect the elements on the HTML to add additional drop down settings.
            # Make sure you set the id as the value

            ttry {
                Select-DropDown -elementId $elementId -selectedValue $matchedActivityType.Value
                # We are using Views of answers to dertmine if the Javascript has ran
                Wait-ForJavascript -ElementText 'Views of answers'
            } -Catch {       
                # If the Javascript Fails to Populate the Sub entries within the form
                # it will retrigger by select the "Chef/Puppet in Datacenter"
                Start-Sleep -Seconds 1
                Select-DropDown -elementId $Script:ContributionAreas[-1].elementId -selectedValue $LocalizedData.ElementValueChefPuppetInDataCenter   
            } -RetryLimit 10     
        }   
    }
}
#BuildFileName: Get-AreaNamedValues.ps1
function Get-AreaNamedValues {
<#
.Description
This command is used to retrieve the list of fields that are required for an MVP submission.
Since different MVP Area's have other fields, you can use Get-AreaNamedValues 'AreaName' to identify the fields.

In the example below, we will use Get-AreaNamedValues 'Article' to get the Value names:

Get-AreaNamedValues Article

Name Mandatory
---- ---------
Number of Articles True
Title True
Date True
Number of Views False
URL False
Description False

Once the list is returned, we can construct the data structure using the output:

MVPActivity "Test" {
    
    # Let's set the Area and the Contribution Area
    Area 'Article'
    ContributionArea 'PowerShell'

    # We can set the mandatory parameters
    Value "Number of Articles" 1
    Value Title "Test Entry"
    Value Date "30/11/2020"

}

.PARAMETER AreaName
The MVP Contribution Area name to lookup.

.EXAMPLE
In the example below, we will use Get-AreaNamedValues 'Article' to get the Value names:

Get-AreaNamedValues Article

Name Mandatory
---- ---------
Number of Articles True
Title True
Date True
Number of Views False
URL False
Description False

.SYNOPSIS
This command is used to retrive the list of fields that are required for a MVP submission.
#>

    [CmdletBinding()]
    param (
        [Parameter()]
        [String]
        $AreaName
    )

    # Call the HTML FormStrucutre and Parse in the Article Name
    Get-HTMLFormStructure $AreaName | Select-Object Name, @{Name='Mandatory';Exp={$_.isRequired}} | Sort-Object -Property Mandatory -Descending

}
#BuildFileName: MVPActivity.ps1
function MVPActivity {
<#
.Description
MVPActivity is the top-level definition command, which groups the MVP contribution types into their respective areas.

This command is responsible for buildup (creation) and (tear-down) of the contribution. There are two types of input with this command:

1. CSV Import and,
2. The Domain Specific Language.

The CSV Import is a 'simplified' input parameter, however *each* CSV import is limited to a seperate 'Area'.
If you choose to use the Domain Specific Language type, commands: 'Area', 'ContributionArea' and 'Value' are mandatory.
MVPActivity also performs input validation ensuring that input meets the requirements of the HTML form.

.PARAMETER Name
Name is the name of the MVPActivity. This provides a visual grouping of data that is being added to the MVP Portal.

.PARAMETER CSVPath
Import the CSV File and Parse it.
Please Note: *The headers 'Area' & 'ContributionArea' are mandatory.*
Adding Secondary and Tertiary 'ContributionAreas' is done by using: 'SecondContributionArea' and 'ThirdContributionArea' (See Example Below)

    MVPActivity -CSVPath 'Path to CSV File'

Example CSV File:

Date,Title,URL,Description,Number of Articles,Number of Views,Area,ContributionArea,SecondContributionArea,ThirdContributionArea
28/11/2020,TEST,https://www.google.com,TEST,1,1,Article,PowerShell,Networking,Storage
28/11/2020,TEST,https://www.google.com,TEST,1,1,Article,PowerShell,Networking,Storage

.PARAMETER ArgumentList
MVPActivity supports arguments being parsed into it using the -ArgumentList parameter, similarly to the -TestCases parameter within Pester.
Inputs are defined as a [HashTable] or a [HashTable[]] with the name of the parameter being the [HashTable] key and it's value being the item.
To use the parameters that have been parsed into the fixture, use the param() block at the beginning of the fixture with the ParameterName.

In the example below we declare the parameters and parse them into MVPActivity:

# Declare the parameters:
$param = @{
    Parameter1 = 'Test'
}

# Parse the Arguments by using the -ArgumentList parameter:
MVPActivity "Test Entry" -ArgumentList $param {
    # To get the value from $Parameter1, add a param() within the fixture/scriptblock.
    param($Parameter1)

    # We can reference the value by referencing the variable name '$Parameter1'
    Area $Parameter1 'Value'

}

The -ArgumentList parameter also supports an array of hashtables ([HashTable[]]) with common values, providing a way to parse multiple parameters into the same fixture.
For Example:

# Declare the parameters as an array:
$param = @(
    @{
        Parameter1 = 'One Value'
    }
    @{
        Parameter1 = 'Another Value'
    }
)

MVPActivity "Test Entry" -ArgumentList $param {
    param($Parameter1)

    Area $Parameter1 'Value'

}

This will process 'Parameter1' as a seperate MVP Contribution.

.PARAMETER Fixture
The Fixture/Scriptblock contains the DSL PowerShell code that adds the MVP entry to the portal.
The Fixture is a regular Powershell scriptblock, however Area, ContributionArea and Value are mandatory commands.

*Please ensure that 'Area', 'ContributionArea' are prefixed before 'Value'*.

.EXAMPLE
Import the CSV File and Parse it:

MVPActivity -CSVPath 'Path to CSV File'

.EXAMPLE
Standard Usage:

    MVPActivity "Reddit Contribution" {

        Area 'Article'
        # ContributionArea can accept an Array
        ContributionArea 'PowerShell','Yammer','Word'

        Value 'Date' $date
        Value 'Title' 'TEST'
        Value 'URL' 'https:\\test.com'
        Value 'Description' 'THIS IS A TEST'
        Value 'Number of Posts' '1'
        # Or you can use the HTML DivId
        Value 'Number of Subscribers' '1'
        Value 'Annual Unique Visitors' '1'

    }

.EXAMPLE
In this example below, we will parse a hashtable of arguments into the fixture:

    $params = @{
        Area = "Blog/Website Post"
        ContributionArea = "PowerShell","Yammer","Word"
        date = '26/10/2020'
    }

    # Define Activity
    MVPActivity "Name of Activity" -ArgumentList $params {
        # If the Parameters -Area or -ContributionArea are defined
        # in the param block, the cmdlet is not required (Area or ContributionArea).
        param($Area, $ContributionArea, $date)

        # You can use the String Name
        Value 'Date' $date
        Value 'Title' 'TEST'
        Value 'URL' 'https:\\test.com'
        Value 'Description' 'THIS IS A TEST'
        Value 'Number of Posts' '1'
        # Or you can use the HTML Div Id
        Value 'Number of Subscribers' '1'
        Value 'Annual Unique Visitors' '1'
        # Still can execute PowerShell within the script block
        Start-Sleep -Seconds 3

   }
.EXAMPLE

For those who love complexity, the fixture supports an array of HashTables to be parsed into the fixture.

    $params = @(
        @{
            Area = "Blog/Website Post"
            ContributionArea = "PowerShell","Yammer","Word"
            date = '26/10/2020'
        }
        @{
            Area = "Blog/Website Post"
            ContributionArea = "PowerShell","Yammer","Word"
            date = '15/10/2020'
        }
    )

    # Define Activity
    MVPActivity "Name of Activity" -ArgumentList $params {
        # If the Parameters -Area or -ContributionArea are defined in the param block,
        # the cmdlet is not required (Area or ContributionArea).
        param($Area, $ContributionArea, $date)

        # You can use the String Name
        Value 'Date' $date
        Value 'Title' 'TEST'
        Value 'URL' 'https:\\test.com'
        Value 'Description' 'THIS IS A TEST'
        Value 'Number of Posts' '1'
        # Or you can use the HTML Div Id
        Value 'Number of Subscribers' '1'
        Value 'Annual Unique Visitors' '1'
        # Still can execute PowerShell within the script block
        Start-Sleep -Seconds 3

   }


.SYNOPSIS
MVPActivity is the top-level definition command, which groups the MVP contribution types into their respective areas.
#>
    
    [CmdletBinding(DefaultParameterSetName="Default")]
    param (        
        # Scriptblock of the Name of the Contribution
        [Parameter(Mandatory,Position=1,ParameterSetName="Default")]
        [Parameter(Mandatory,Position=1,ParameterSetName="Arguments")]
        [Parameter(Position=1,ParameterSetName="CSVFile")]
        [String]
        $Name,
        # Scriptblock of the Activity
        [Parameter(Mandatory,Position=2,ParameterSetName="Arguments")]
        [Parameter(Mandatory,Position=2,ParameterSetName="Default")]
        [ScriptBlock]
        $Fixture,
        # ArgumentList of the Activity
        [Parameter(Position=3,ParameterSetName="Arguments")]
        [HashTable[]]
        $ArgumentList,
        # CSV File
        [Parameter(Position=2,ParameterSetName="CSVFile")]
        [String]
        $CSVPath
    )

    # Construct the Parameters to Invoke
    $params = @{}

    switch ($PSCmdlet.ParameterSetName) {
        "Arguments" {

            $params.Fixture = $Fixture
            
        }
        "CSVFile" {

            # Test the CSV Schema to ensure that all the columns are present
            Test-CSVSchema $CSVPath
            # Create the Fixture
            $params.Fixture = New-CSVFixture $CSVPath
            # Create the Arguements for the Fixture
            $ArgumentList = New-CSVArguments $CSVPath
            
        }
        default {

            # If the Default Execution is run, execute the Fixture and then return.
            $params.Fixture = $Fixture

        }
    }

    # Write-Host
    if ($Name) { Write-Host ('Executing MVPActivity: "{0}"' -f $Name) -ForegroundColor Green }
    else { Write-Host ('Executing MVPActivity: (From CSV File) "{0}"' -f $CSVPath) -ForegroundColor Green }

    # If Arguments are Present, then iterate each Fixture
    if ($ArgumentList) {
        # Iterate Through Each of the Arguments and Create the MVPActivity
        foreach($Argument in $ArgumentList) {

            $params.ArgumentList = $Argument
            
            ttry {
                
                #Adding more feedback to the user.
                Write-Host ("[ADDING] TITLE: '{0}' - DATE: '{1}'" -f $params.ArgumentList.Title, $params.ArgumentList.Date)
                New-MVPActivity @params

            } -catch {

                Write-Warning "[ERROR] Failed to Add Activity (Most likley to bad Javascript). Retrying."
                # Refresh the page
                Enter-SeUrl 'https://mvp.microsoft.com/en-us/MyProfile/EditActivity' -Driver $Global:MVPDriver
                Start-Sleep -Seconds 5

            }
            
        }

    } else {

        # Otherwise Execute the MVP Activity, since there is not additional fixtures.
        New-MVPActivity @params

    }


}
#BuildFileName: New-MVPFixture.ps1
function New-MVPFixture {
<#
.Description
New-MVPFixture is a utility that generates MVPActivity ScriptBlocks.

.PARAMETER AreaName
The MVP Contribution Area name to lookup.

.PARAMETER MVPActivityName
The MVP Activity Name.

.EXAMPLE
Standard Execution:

New-MVPFixture -AreaName 'Article' -MVPActivityName 'Test'

.SYNOPSIS
New-MVPFixture is a utility that generates MVPActivity ScriptBlocks.
#>

    [CmdletBinding()]
    param (
        [String]
        $AreaName=(Read-Host "Enter in an AreaName"),
        [String]
        $MVPActivityName=(Read-Host "Enter in an MVPActivity Name")
    )

    $FormStructure = Get-AreaNamedValues -AreaName $AreaName

    return ("
+==================== START COPY ====================+
MVPActivity '$MVPActivityName' {
    # Param Block for arguments to be parsed in.
    param()

    # Define the Area
    Area '$AreaName'
    # Define the Contribution Area
    ContributionArea 'Contribution Area 1', 'Contribution Area 2', 'Contribution Area 3'

    # Define the Input Values:

    $(
        $FormStructure | ForEach-Object {
            "`n",
            " #Mandatory: $($_.Mandatory) `n",
            " Value '$($_.Name)' '' `n"
        }
    )
}
+==================== STOP COPY ====================+
    "
)
    
}
#BuildFileName: Value.ps1
Function Value {
<#
The 'Value' command is used as input entries for the HTML form, using 'Name' (Being the HTML Div Element ID or Text Name) and 'Value' (Corresponding Value) syntax.
Since different 'Area''s have various fields, you can use Get-AreaNamedValues 'AreaName' to locate those fields.

    Value is mandatory within MVPActivity and won't be automatically invoked when specified within the Param() bock.

Value supports auto-formatting of inputted data to meet the requirements of the MVP Portal. Currently, the following Portal dependencies are auto-formatted:

    Date (Required to be US Date Format) (MM-DD-YYYY)
    URL (Required to meet URL format) (https://site.com)

To view the list of Values for an MVP Contribution Area, use: Get-AreaNamedValues 'Name' (Get-AreaNamedValues 'Article')

.PARAMETER Name
The HTML Div Element ID or Text Name

.PARAMETER Value
The input value.

.EXAMPLE
In the example, 'Value' is being used to set the HTML values for the 'Article' MVP Contribution Area.

MVPActivity "Test" {
    
    # Let's set the Area and the Contribution Area
    Area 'Article'

    # We can set the mandatory parameters
    Value "Number of Articles" 1
    Value Title "Test Entry"
    Value Date "30/11/2020"

}

.SYNOPSIS
The Value command is used to input the data into the HTML form, using the 'Name' (Being the HTML Div Element ID or Text Name) and 'Value' (Corresponding Value) syntax.
#>
       
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]
        $Name,
        [Parameter(Mandatory)]
        [String]
        $Value
    )
      
    # Test if the Driver is active. If not throw a terminating error.
    Test-SEDriver
    # Test the Callstack.
    Test-CallStack $PSCmdlet.MyInvocation.MyCommand.Name   

    # Get the HTML Element Data
    $htmlElementData = $Script:MVPHTMLFormStructure

    # Attempt to Locate the HTML Element that is needed to update.
    # If there is no value, then there is no data
    [array]$matched = $htmlElementData.Where{($_.Name -eq $Name) -or ($_.Element -eq $Name)}
    # If the Either The Element of the String name can't be found,
    # then Throw a terminating error.
    if ($matched.count -eq 0) {
        Throw ($LocalizedData.ErrorCannotFindHTMLElement -f $Name)
    }
    # If there is more then 1 result, data duplicate. Throw temrinating error.
    if ($matched.count -ne 1) {
        Throw ($LocalizedData.ErrorTooManyHTMLElements -f $Name, $matched.count)
    }    
    
    $params = @{
        Keys = $Value
    }

    # If there is a formatting property, then we invoke the scriptblock and format the data
    if ($matched.Format) {

        try {
            # Invoke the Scriptblock
            $params.Keys = $matched.Format.Invoke($Value)
        } catch {
            # Throw an Error if there was a problem.
            Throw ($LocalizedData.ErrorFormattingValue -f $Name, $Value, $_.ToString())
        }
        
    }

    # Fetches the Element and Updates the Field
    $Element = Find-SeElement -Driver ($Global:MVPDriver) -Id $matched[0].Element
    if ($null -eq $Element) { return }
    Send-SeKeys $Element @params

    # Locate and Update the HTMLElement
    $matched[0].isSet = $true

}
#BuildFileName: Visibility.ps1
Function Visibility {
<#
The 'Visibility' Parameter sets the visibility of the Activity.
This statement can be included within the fixture or executed within the Param Block, similar to 'Area' and 'Contribution Area'.
Multiple statements aren't allowed within the Fixture.

.PARAMETER Name
The Visibility Name: Values can be: 'Everyone','MVP Community' & 'Microsoft'.

.EXAMPLE
In the example, 'Visibility' is set within the Fixture:

MVPActivity "Test" {
    
    # Let's set the Area and the Contribution Area
    Area 'Article'
    Visibility 'Microsoft'

    # We can set the mandatory parameters
    Value "Number of Articles" 1
    Value Title "Test Entry"
    Value Date "30/11/2020"

}

.EXAMPLE
In the example, 'Visibility' is set within the param block:

MVPActivity "Test" {
    param($Visibility)
    
    # Let's set the Area and the Contribution Area
    Area 'Article'

    # We can set the mandatory parameters
    Value "Number of Articles" 1
    Value Title "Test Entry"
    Value Date "30/11/2020"

}

.SYNOPSIS
Set's the Visibility of the entry.

#>
     
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]
        [ValidateSet('Everyone','MVP Community','Microsoft')]
        $Name
    )

    # Test if the Driver is active. If not throw a terminating error.
    Test-SEDriver
    # Test the Callstack.
    Test-CallStack $PSCmdlet.MyInvocation.MyCommand.Name   

    # Locate the Visability Element.
    $VisibilityElement = Find-SeElement -Driver ($Global:MVPDriver) -By XPath -Selection $LocalizedData.ElementVisibilityBoxXPath
    if ($null -eq $VisibilityElement) { return }

    # Match the Name of the Element. If the name is the same, skip!
    if ($Name -eq $VisibilityElement.Text) { return }

    # Select the Element and Select the Correct Item
    Invoke-SeClick -Element $VisibilityElement
    # Locate the ListItem Permission
    $PermissionsParams = @{
        Driver = $Global:MVPDriver
        By = 'XPath'
        Selection = $LocalizedData.VisibilityListItem -f $Name
    }
    $PermissionElement = Find-SeElement @PermissionsParams

    # Select the DropDown Item
    Invoke-SeClick -Element $PermissionElement
    
}