rautomation 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e50cb69e80b4659265b0edfc11904e064efee3b
4
- data.tar.gz: 6ea9b5a0d4cfc8c2c43ddd995c7a0c8b07ccff7e
3
+ metadata.gz: 9f790814402e5dff056890bfd0bc89e90025b5ab
4
+ data.tar.gz: f0a96181d6654fd874cfa2e7c05da15f139bdf56
5
5
  SHA512:
6
- metadata.gz: 6bbe2f8ed80c1dff85044ce04fba2e62fac3489045d4cadaf792322f70eac4a9f06e08a4d86e6ec0e3afabc9ac6559e166f75397976b1af1f3ed76874976e88f
7
- data.tar.gz: 2400713a652d47652e93249c2a38dcd8e809f46a619ea986762bada8d561f13e2eed3dff293eb2fc84020654efdf728599492a431026a6729b97543bd545578f
6
+ metadata.gz: c258b23e4d6b47cc5708590cbdf36a991ce51c30924e2d547f88066a097a2585447359daef244e34153c350bb8577e0287d8e9fc15acbc82575edde0e36931d8
7
+ data.tar.gz: b74d867f6e09501da80d26cc1852145b8206fc1b21d46165cd92cd9c7c96589cdb887f4298629e6779c0ad16ab2c616cc7e92263e72d9ca2eb94f77de89d4deb
data/.gitignore CHANGED
@@ -33,3 +33,4 @@ ipch
33
33
  *.user
34
34
  *.opensdf
35
35
  _ReSharper*/
36
+ packages/
@@ -1,23 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rautomation (0.13.0)
4
+ rautomation (0.14.0)
5
5
  ffi
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- diff-lcs (1.1.3)
10
+ diff-lcs (1.2.4)
11
11
  ffi (1.9.0-x86-mingw32)
12
12
  rake (0.9.2.2)
13
- rspec (2.8.0)
14
- rspec-core (~> 2.8.0)
15
- rspec-expectations (~> 2.8.0)
16
- rspec-mocks (~> 2.8.0)
17
- rspec-core (2.8.0)
18
- rspec-expectations (2.8.0)
19
- diff-lcs (~> 1.1.2)
20
- rspec-mocks (2.8.0)
13
+ rspec (2.14.1)
14
+ rspec-core (~> 2.14.0)
15
+ rspec-expectations (~> 2.14.0)
16
+ rspec-mocks (~> 2.14.0)
17
+ rspec-core (2.14.6)
18
+ rspec-expectations (2.14.3)
19
+ diff-lcs (>= 1.1.3, < 2.0)
20
+ rspec-mocks (2.14.4)
21
21
  yard (0.8.6.1)
22
22
 
23
23
  PLATFORMS
@@ -26,5 +26,5 @@ PLATFORMS
26
26
  DEPENDENCIES
27
27
  rake
28
28
  rautomation!
29
- rspec (~> 2.3)
29
+ rspec (~> 2.14)
30
30
  yard
@@ -1,3 +1,14 @@
1
+ == 0.14.0
2
+
3
+ === MsUia adapter
4
+
5
+ * Add SelectList#select and SelectList#clear.
6
+ * Add Table#select and Table#clear.
7
+ * Add Table#selected_rows.
8
+ * Improve performance of table Row lookups.
9
+ * SelectList#options accepts :index and :text (Regexp or String).
10
+ * Window#child in ms_uia works for popup windows as well.
11
+
1
12
  == 0.13.0
2
13
 
3
14
  === MsUia adapter
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.13.0
1
+ 0.14.0
@@ -26,6 +26,11 @@ namespace RAutomation.UIA.Controls
26
26
  set { Select(value); }
27
27
  }
28
28
 
29
+ public int[] SelectedIndexes
30
+ {
31
+ get { return DataItems.IndexesOf(x => x.AsSelectionItem().Current.IsSelected); }
32
+ }
33
+
29
34
  public bool IsRowSelected(int dataItemIndex)
30
35
  {
31
36
  return DataItems.ElementAt(dataItemIndex).AsSelectionItem().Current.IsSelected;
@@ -71,6 +76,11 @@ namespace RAutomation.UIA.Controls
71
76
  get { return TableOrListItems.Select(x => x.Current.Name).ToArray(); }
72
77
  }
73
78
 
79
+ public void SingleSelect(int value)
80
+ {
81
+ DataItems.ElementAt(value).AsSelectionItem().Select();
82
+ }
83
+
74
84
  private void Select(int value)
75
85
  {
76
86
  var selectionItem = DataItems.ElementAt(value).AsSelectionItem();
@@ -49,6 +49,19 @@ namespace RAutomation.UIA.Extensions
49
49
  return -1;
50
50
  }
51
51
 
52
+ public static int[] IndexesOf<T>(this IEnumerable<T> items, Func<T, bool> trueCondition)
53
+ {
54
+ var indexes = new List<int>();
55
+ var foundIndex = 0;
56
+ foreach (var item in items)
57
+ {
58
+ if (trueCondition(item)) indexes.Add(foundIndex);
59
+ ++foundIndex;
60
+ }
61
+
62
+ return indexes.ToArray();
63
+ }
64
+
52
65
  public static void ForEach<T>(this IEnumerable<T> items, Action<T> doIt)
53
66
  {
54
67
  foreach (var item in items)
@@ -0,0 +1,14 @@
1
+ #include "StdAfx.h"
2
+ #include "ArrayHelper.h"
3
+
4
+
5
+ int ArrayHelper::Copy(array<int, 1>^ source, int destination[])
6
+ {
7
+ if( NULL != destination ) {
8
+ auto index = 0;
9
+ for each(auto value in source) {
10
+ destination[index++] = value;
11
+ }
12
+ }
13
+ return source->Length;
14
+ }
@@ -0,0 +1,7 @@
1
+ #pragma once
2
+ ref class ArrayHelper
3
+ {
4
+ public:
5
+ static int Copy(array<int>^ source, int destination[]);
6
+ };
7
+
@@ -1,58 +1,73 @@
1
1
  #include "stdafx.h"
2
2
  #include "Locator.h"
3
+ #include "ArrayHelper.h"
3
4
  #include "StringHelper.h"
4
5
 
5
6
  using namespace RAutomation::UIA::Controls;
6
7
 
7
8
  extern "C" {
8
9
 
9
- __declspec ( dllexport ) int Table_GetHeaders(const FindInformation& findInformation, const char* headers[]) {
10
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
11
- return StringHelper::Copy(tableControl->Headers, headers);
12
- }
10
+ __declspec ( dllexport ) int Table_GetHeaders(const FindInformation& findInformation, const char* headers[]) {
11
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
12
+ return StringHelper::Copy(tableControl->Headers, headers);
13
+ }
14
+
15
+ __declspec ( dllexport ) int Table_GetValues(const FindInformation& findInformation, const char* values[]) {
16
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
17
+ return StringHelper::Copy(tableControl->Values, values);
18
+ }
13
19
 
14
- __declspec ( dllexport ) int Table_GetValues(const FindInformation& findInformation, const char* values[]) {
15
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
16
- return StringHelper::Copy(tableControl->Values, values);
17
- }
20
+ __declspec ( dllexport ) int Table_GetSelectedIndexes(const FindInformation& findInformation, int selectedIndexes[]) {
21
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
22
+ return ArrayHelper::Copy(tableControl->SelectedIndexes, selectedIndexes);
23
+ }
18
24
 
19
- __declspec ( dllexport ) int Table_RowCount(const FindInformation& findInformation) {
20
- try {
21
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
22
- return tableControl->RowCount;
23
- } catch(Exception^ e) {
24
- Console::WriteLine(e->ToString());
25
+ __declspec ( dllexport ) int Table_RowCount(const FindInformation& findInformation) {
26
+ try {
27
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
28
+ return tableControl->RowCount;
29
+ } catch(Exception^ e) {
30
+ Console::WriteLine(e->ToString());
25
31
  return 0;
26
- }
27
- }
32
+ }
33
+ }
28
34
 
29
- __declspec ( dllexport ) bool Table_CoordinateIsValid(const FindInformation& findInformation, const int whichItemIndex, const int whichColumnIndex) {
30
- try {
31
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
32
- return tableControl->Exists(whichItemIndex, whichColumnIndex);
33
- } catch(Exception^ e) {
34
- Console::WriteLine(e->ToString());
35
- return false;
36
- }
37
- }
35
+ __declspec ( dllexport ) bool Table_CoordinateIsValid(const FindInformation& findInformation, const int whichItemIndex, const int whichColumnIndex) {
36
+ try {
37
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
38
+ return tableControl->Exists(whichItemIndex, whichColumnIndex);
39
+ } catch(Exception^ e) {
40
+ Console::WriteLine(e->ToString());
41
+ return false;
42
+ }
43
+ }
44
+
45
+ __declspec ( dllexport ) void Table_ValueAt(const FindInformation& findInformation, const int row, const int column, char *foundValue, const int foundValueLength) {
46
+ try {
47
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
48
+ StringHelper::CopyToUnmanagedString(tableControl->ValueAt(row, column), foundValue, foundValueLength);
49
+ } catch(Exception^ e) {
50
+ Console::WriteLine(e->ToString());
51
+ }
52
+ }
38
53
 
39
- __declspec ( dllexport ) void Table_ValueAt(const FindInformation& findInformation, const int row, const int column, char *foundValue, const int foundValueLength) {
40
- try {
41
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
42
- StringHelper::CopyToUnmanagedString(tableControl->ValueAt(row, column), foundValue, foundValueLength);
43
- } catch(Exception^ e) {
44
- Console::WriteLine(e->ToString());
45
- }
46
- }
54
+ __declspec ( dllexport ) void Table_SelectByIndex(const FindInformation& findInformation, const int dataItemIndex, char* errorInfo, const int errorInfoLength) {
55
+ try {
56
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
57
+ tableControl->SelectedIndex = dataItemIndex;
58
+ } catch(Exception^ e) {
59
+ StringHelper::Write(e, errorInfo, errorInfoLength);
60
+ }
61
+ }
47
62
 
48
- __declspec ( dllexport ) void Table_SelectByIndex(const FindInformation& findInformation, const int dataItemIndex, char* errorInfo, const int errorInfoLength) {
49
- try {
50
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
51
- tableControl->SelectedIndex = dataItemIndex;
52
- } catch(Exception^ e) {
63
+ __declspec ( dllexport ) void Table_SingleSelectByIndex(const FindInformation& findInformation, const int dataItemIndex, char* errorInfo, const int errorInfoLength) {
64
+ try {
65
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
66
+ tableControl->SingleSelect(dataItemIndex);
67
+ } catch(Exception^ e) {
53
68
  StringHelper::Write(e, errorInfo, errorInfoLength);
54
- }
55
- }
69
+ }
70
+ }
56
71
 
57
72
  __declspec ( dllexport ) void Table_RemoveRowByIndex(const FindInformation& findInformation, const int dataItemIndex, char* errorInfo, const int errorInfoLength) {
58
73
  try {
@@ -72,22 +87,22 @@ extern "C" {
72
87
  }
73
88
  }
74
89
 
75
- __declspec ( dllexport ) bool Table_IsSelectedByIndex(const FindInformation& findInformation, const int dataItemIndex) {
76
- try {
77
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
90
+ __declspec ( dllexport ) bool Table_IsSelectedByIndex(const FindInformation& findInformation, const int dataItemIndex) {
91
+ try {
92
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
78
93
  return tableControl->IsRowSelected(dataItemIndex);
79
- } catch(Exception^ e) {
80
- Console::WriteLine(e->ToString());
94
+ } catch(Exception^ e) {
95
+ Console::WriteLine(e->ToString());
81
96
  return false;
82
- }
83
- }
97
+ }
98
+ }
84
99
 
85
- __declspec ( dllexport ) void Table_SelectByValue(const FindInformation& findInformation, const char* dataItemValue, char* errorInfo, const int errorInfoLength) {
86
- try {
87
- auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
88
- tableControl->Value = gcnew String(dataItemValue);
89
- } catch(Exception^ e) {
100
+ __declspec ( dllexport ) void Table_SelectByValue(const FindInformation& findInformation, const char* dataItemValue, char* errorInfo, const int errorInfoLength) {
101
+ try {
102
+ auto tableControl = gcnew TableControl(Locator::FindFor(findInformation));
103
+ tableControl->Value = gcnew String(dataItemValue);
104
+ } catch(Exception^ e) {
90
105
  StringHelper::Write(e, errorInfo, errorInfoLength);
91
- }
92
- }
106
+ }
107
+ }
93
108
  }
@@ -80,6 +80,7 @@
80
80
  <Reference Include="WindowsBase" />
81
81
  </ItemGroup>
82
82
  <ItemGroup>
83
+ <ClInclude Include="ArrayHelper.h" />
83
84
  <ClInclude Include="Locator.h" />
84
85
  <ClInclude Include="DynamicAssemblyResolver.h" />
85
86
  <ClInclude Include="MenuItemSelector.h" />
@@ -90,6 +91,7 @@
90
91
  <ClInclude Include="UiaDll.h" />
91
92
  </ItemGroup>
92
93
  <ItemGroup>
94
+ <ClCompile Include="ArrayHelper.cpp" />
93
95
  <ClCompile Include="AssemblyInfo.cpp" />
94
96
  <ClCompile Include="Locator.cpp" />
95
97
  <ClCompile Include="ControlMethods.cpp" />
@@ -39,6 +39,9 @@
39
39
  <ClInclude Include="MenuItemSelector.h">
40
40
  <Filter>Header Files</Filter>
41
41
  </ClInclude>
42
+ <ClInclude Include="ArrayHelper.h">
43
+ <Filter>Header Files</Filter>
44
+ </ClInclude>
42
45
  </ItemGroup>
43
46
  <ItemGroup>
44
47
  <ClCompile Include="UiaDll.cpp">
@@ -89,6 +92,9 @@
89
92
  <ClCompile Include="SpinnerMethods.cpp">
90
93
  <Filter>Source Files</Filter>
91
94
  </ClCompile>
95
+ <ClCompile Include="ArrayHelper.cpp">
96
+ <Filter>Source Files</Filter>
97
+ </ClCompile>
92
98
  </ItemGroup>
93
99
  <ItemGroup>
94
100
  <None Include="ReadMe.txt" />
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <configuration>
3
+ <solution>
4
+ <add key="disableSourceControlIntegration" value="true" />
5
+ </solution>
6
+ </configuration>
@@ -0,0 +1,136 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <PropertyGroup>
4
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
5
+
6
+ <!-- Enable the restore command to run before builds -->
7
+ <RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
8
+
9
+ <!-- Property that enables building a package from a project -->
10
+ <BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
11
+
12
+ <!-- Determines if package restore consent is required to restore packages -->
13
+ <RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">false</RequireRestoreConsent>
14
+
15
+ <!-- Download NuGet.exe if it does not already exist -->
16
+ <DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
17
+ </PropertyGroup>
18
+
19
+ <ItemGroup Condition=" '$(PackageSources)' == '' ">
20
+ <!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
21
+ <!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
22
+ <!--
23
+ <PackageSource Include="https://www.nuget.org/api/v2/" />
24
+ <PackageSource Include="https://my-nuget-source/nuget/" />
25
+ -->
26
+ </ItemGroup>
27
+
28
+ <PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
29
+ <!-- Windows specific commands -->
30
+ <NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
31
+ <PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
32
+ </PropertyGroup>
33
+
34
+ <PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
35
+ <!-- We need to launch nuget.exe with the mono command if we're not on windows -->
36
+ <NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
37
+ <PackagesConfig>packages.config</PackagesConfig>
38
+ </PropertyGroup>
39
+
40
+ <PropertyGroup>
41
+ <!-- NuGet command -->
42
+ <NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
43
+ <PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
44
+
45
+ <NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
46
+ <NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
47
+
48
+ <PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
49
+
50
+ <RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
51
+ <NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
52
+
53
+ <PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
54
+ <PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
55
+
56
+ <!-- Commands -->
57
+ <RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
58
+ <BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
59
+
60
+ <!-- We need to ensure packages are restored prior to assembly resolve -->
61
+ <BuildDependsOn Condition="$(RestorePackages) == 'true'">
62
+ RestorePackages;
63
+ $(BuildDependsOn);
64
+ </BuildDependsOn>
65
+
66
+ <!-- Make the build depend on restore packages -->
67
+ <BuildDependsOn Condition="$(BuildPackage) == 'true'">
68
+ $(BuildDependsOn);
69
+ BuildPackage;
70
+ </BuildDependsOn>
71
+ </PropertyGroup>
72
+
73
+ <Target Name="CheckPrerequisites">
74
+ <!-- Raise an error if we're unable to locate nuget.exe -->
75
+ <Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
76
+ <!--
77
+ Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
78
+ This effectively acts as a lock that makes sure that the download operation will only happen once and all
79
+ parallel builds will have to wait for it to complete.
80
+ -->
81
+ <MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
82
+ </Target>
83
+
84
+ <Target Name="_DownloadNuGet">
85
+ <DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
86
+ </Target>
87
+
88
+ <Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
89
+ <Exec Command="$(RestoreCommand)"
90
+ Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
91
+
92
+ <Exec Command="$(RestoreCommand)"
93
+ LogStandardErrorAsError="true"
94
+ Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
95
+ </Target>
96
+
97
+ <Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
98
+ <Exec Command="$(BuildCommand)"
99
+ Condition=" '$(OS)' != 'Windows_NT' " />
100
+
101
+ <Exec Command="$(BuildCommand)"
102
+ LogStandardErrorAsError="true"
103
+ Condition=" '$(OS)' == 'Windows_NT' " />
104
+ </Target>
105
+
106
+ <UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
107
+ <ParameterGroup>
108
+ <OutputFilename ParameterType="System.String" Required="true" />
109
+ </ParameterGroup>
110
+ <Task>
111
+ <Reference Include="System.Core" />
112
+ <Using Namespace="System" />
113
+ <Using Namespace="System.IO" />
114
+ <Using Namespace="System.Net" />
115
+ <Using Namespace="Microsoft.Build.Framework" />
116
+ <Using Namespace="Microsoft.Build.Utilities" />
117
+ <Code Type="Fragment" Language="cs">
118
+ <![CDATA[
119
+ try {
120
+ OutputFilename = Path.GetFullPath(OutputFilename);
121
+
122
+ Log.LogMessage("Downloading latest version of NuGet.exe...");
123
+ WebClient webClient = new WebClient();
124
+ webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
125
+
126
+ return true;
127
+ }
128
+ catch (Exception ex) {
129
+ Log.LogErrorFromException(ex);
130
+ return false;
131
+ }
132
+ ]]>
133
+ </Code>
134
+ </Task>
135
+ </UsingTask>
136
+ </Project>
@@ -1,8 +1,15 @@
1
1
  
2
- Microsoft Visual Studio Solution File, Format Version 11.00
3
- # Visual Studio 2010
2
+ Microsoft Visual Studio Solution File, Format Version 12.00
3
+ # Visual Studio 2012
4
4
  Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsForms", "WindowsForms\WindowsForms.csproj", "{79C33EDA-58C4-41EF-93DC-BA679895BD43}"
5
5
  EndProject
6
+ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C2B59FF1-0EE8-457F-A808-3B558459DA57}"
7
+ ProjectSection(SolutionItems) = preProject
8
+ .nuget\NuGet.Config = .nuget\NuGet.Config
9
+ .nuget\NuGet.exe = .nuget\NuGet.exe
10
+ .nuget\NuGet.targets = .nuget\NuGet.targets
11
+ EndProjectSection
12
+ EndProject
6
13
  Global
7
14
  GlobalSection(SolutionConfigurationPlatforms) = preSolution
8
15
  Debug|Any CPU = Debug|Any CPU
@@ -1,7 +1,7 @@
1
1
  using System;
2
- using System.Linq;
3
2
  using System.Windows.Forms;
4
3
  using FizzWare.NBuilder;
4
+ using UIA.Extensions;
5
5
 
6
6
  namespace WindowsForms
7
7
  {
@@ -10,6 +10,7 @@ namespace WindowsForms
10
10
  public DataGridView()
11
11
  {
12
12
  InitializeComponent();
13
+ dataGridView1.AsTable();
13
14
  }
14
15
 
15
16
  public class Person
@@ -13,6 +13,8 @@
13
13
  <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
14
14
  <TargetFrameworkProfile>Client</TargetFrameworkProfile>
15
15
  <FileAlignment>512</FileAlignment>
16
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
17
+ <RestorePackages>true</RestorePackages>
16
18
  </PropertyGroup>
17
19
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
18
20
  <PlatformTarget>x86</PlatformTarget>
@@ -47,9 +49,8 @@
47
49
  <Reference Include="System.Drawing" />
48
50
  <Reference Include="System.Windows.Forms" />
49
51
  <Reference Include="System.Xml" />
50
- <Reference Include="UIA.Extensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
51
- <SpecificVersion>False</SpecificVersion>
52
- <HintPath>.\UIA.Extensions.dll</HintPath>
52
+ <Reference Include="UIA.Extensions">
53
+ <HintPath>..\packages\UIA.Extensions.1.0.5.0\lib\net35\UIA.Extensions.dll</HintPath>
53
54
  </Reference>
54
55
  <Reference Include="UIAutomationClient" />
55
56
  <Reference Include="UIAutomationProvider" />
@@ -123,6 +124,7 @@
123
124
  <AutoGen>True</AutoGen>
124
125
  <DependentUpon>Resources.resx</DependentUpon>
125
126
  </Compile>
127
+ <None Include="packages.config" />
126
128
  <None Include="Properties\Settings.settings">
127
129
  <Generator>SettingsSingleFileGenerator</Generator>
128
130
  <LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -135,6 +137,7 @@
135
137
  </ItemGroup>
136
138
  <ItemGroup />
137
139
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
140
+ <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
138
141
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
139
142
  Other similar extension points exist, see Microsoft.Common.targets.
140
143
  <Target Name="BeforeBuild">
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <packages>
3
+ <package id="UIA.Extensions" version="1.0.5.0" targetFramework="net40-Client" />
4
+ </packages>
@@ -168,20 +168,13 @@ module RAutomation
168
168
  def control_hwnd(window_hwnd, locators)
169
169
  case
170
170
  when locators[:id]
171
- hwnd = UiaDll.cached_hwnd(UiaDll::SearchCriteria.from_locator(window_hwnd, locators))
172
- raise UnknownElementException, "#{locators[:id]} does not exist" if hwnd == 0
173
- hwnd
171
+ UiaDll.cached_hwnd(UiaDll::SearchCriteria.from_locator(window_hwnd, locators))
174
172
  when locators[:point]
175
- hwnd = UiaDll::handle_from_point(*locators[:point])
176
- raise UnknownElementException, "#{locators[:point]} does not exist" if hwnd == 0
177
- hwnd
173
+ UiaDll::handle_from_point(*locators[:point])
178
174
  else
179
- hwnd = find_hwnd(locators, window_hwnd) do |hwnd|
175
+ find_hwnd(locators, window_hwnd) do |hwnd|
180
176
  locators_match?(locators, control_properties(hwnd, locators))
181
177
  end
182
-
183
- raise UnknownElementException, "Element with #{locators.inspect} does not exist" if (hwnd == 0) or (hwnd == nil)
184
- hwnd
185
178
  end
186
179
  end
187
180
 
@@ -11,8 +11,8 @@ module RAutomation
11
11
 
12
12
  def initialize(select_list, text, index)
13
13
  @select_list = select_list
14
- @text = text
15
- @index = index
14
+ @text = text
15
+ @index = index
16
16
  end
17
17
 
18
18
  def selected?
@@ -28,22 +28,30 @@ module RAutomation
28
28
  UiaDll::remove_from_selection @select_list.search_information, @index
29
29
  end
30
30
 
31
+ def self.locators_match?(locators, item)
32
+ locators.all? do |locator, value|
33
+ return item.text =~ value if value.is_a? Regexp
34
+ return item.send(locator) == value
35
+ end
36
+ end
37
+
31
38
  alias_method :set, :select
32
39
  end
33
40
 
34
- def options(options = {})
35
- items = []
41
+ def option(locator)
42
+ options(locator).first
43
+ end
36
44
 
37
- select_options = UiaDll::select_options(search_information)
38
- select_options.each_with_index do |item, item_no|
39
- if options[:text]
40
- items.push(SelectListOption.new(self, item, item_no)) if options[:text] == item
41
- else
42
- items.push(SelectListOption.new(self, item, item_no))
43
- end
44
- end
45
+ def options(locator = {})
46
+ all_options.select { |item| SelectListOption.locators_match? locator, item }
47
+ end
48
+
49
+ def select(locator = {})
50
+ options(locator).each(&:select)
51
+ end
45
52
 
46
- items
53
+ def clear(locator = {})
54
+ options(locator).each(&:clear)
47
55
  end
48
56
 
49
57
  def value
@@ -54,14 +62,6 @@ module RAutomation
54
62
  UiaDll::selections(search_information)
55
63
  end
56
64
 
57
- def option(options)
58
- UiaDll::select_options(search_information).each_with_index do |item, item_no|
59
- return SelectListOption.new(self, item, item_no) if options[:text] == item
60
- end
61
-
62
- nil
63
- end
64
-
65
65
  def exist?
66
66
  super && matches_type?(Constants::UIA_COMBOBOX_CONTROL_TYPE)
67
67
  end
@@ -69,11 +69,16 @@ module RAutomation
69
69
  alias_method :exists?, :exist?
70
70
 
71
71
  private
72
-
73
72
  def item_count
74
73
  UiaDll::select_list_count(search_information)
75
74
  end
76
75
 
76
+ def all_options
77
+ UiaDll::select_options(search_information).each_with_index.map do |item, index|
78
+ SelectListOption.new(self, item, index)
79
+ end
80
+ end
81
+
77
82
  end
78
83
  end
79
84
  end
@@ -41,8 +41,9 @@ module RAutomation
41
41
  cells(locators).first
42
42
  end
43
43
 
44
- def initialize(window, locators)
45
- @search_information = window.search_information
44
+ def initialize(table, locators)
45
+ @table = table
46
+ @search_information = table.search_information
46
47
  @locators = extract(locators)
47
48
  end
48
49
 
@@ -67,7 +68,7 @@ module RAutomation
67
68
  end
68
69
 
69
70
  def exists?
70
- UiaDll::table_coordinate_valid?(search_information, @locators[:index])
71
+ @locators[:index].between? 0, @table.row_count - 1
71
72
  end
72
73
 
73
74
  def self.locators_match?(locators, item)
@@ -98,6 +99,18 @@ module RAutomation
98
99
  end
99
100
  end
100
101
 
102
+ def select(locators={})
103
+ rows(locators).each(&:select)
104
+ end
105
+
106
+ def clear(locators={})
107
+ rows(locators).each(&:clear)
108
+ end
109
+
110
+ def selected_rows
111
+ UiaDll.table_selected_indexes(search_information).map {|index| Row.new(self, index: index) }
112
+ end
113
+
101
114
  def strings
102
115
  headers = UiaDll.table_headers(search_information)
103
116
  values = UiaDll.table_values(search_information)
@@ -94,7 +94,7 @@ module RAutomation
94
94
  def data=(value)
95
95
  case how
96
96
  when :hwnd
97
- self[:data][:int_data] = value
97
+ self[:data][:int_data] = value || 0 # cannot accept nil
98
98
  when :id, :value
99
99
  self[:data][:string_data] = value
100
100
  when :point
@@ -302,6 +302,8 @@ module RAutomation
302
302
  [SearchCriteria.by_ref, :pointer], :int
303
303
  attach_function :Table_GetValues,
304
304
  [SearchCriteria.by_ref, :pointer], :int
305
+ attach_function :Table_GetSelectedIndexes,
306
+ [SearchCriteria.by_ref, :pointer], :int
305
307
  attach_function :table_row_count, :Table_RowCount,
306
308
  [SearchCriteria.by_ref], :int
307
309
  attach_function :Table_CoordinateIsValid,
@@ -310,6 +312,8 @@ module RAutomation
310
312
  [SearchCriteria.by_ref, :int, :int, :pointer, :int], :void
311
313
  attach_function :Table_SelectByIndex,
312
314
  [SearchCriteria.by_ref, :int, :pointer, :int], :void
315
+ attach_function :Table_SingleSelectByIndex,
316
+ [SearchCriteria.by_ref, :int, :pointer, :int], :void
313
317
  attach_function :Table_SelectByValue,
314
318
  [SearchCriteria.by_ref, :string, :pointer, :int], :void
315
319
  attach_function :table_row_is_selected, :Table_IsSelectedByIndex,
@@ -319,6 +323,10 @@ module RAutomation
319
323
  attach_function :Table_RemoveRowByValue,
320
324
  [SearchCriteria.by_ref, :string, :pointer, :int], :void
321
325
 
326
+ def self.table_selected_indexes(search_information)
327
+ integers_from(:Table_GetSelectedIndexes, search_information)
328
+ end
329
+
322
330
  def self.table_select(search_information, which_item)
323
331
  case which_item
324
332
  when Integer
@@ -328,6 +336,10 @@ module RAutomation
328
336
  end
329
337
  end
330
338
 
339
+ def self.table_single_select(search_information, which_item)
340
+ can_throw(:Table_SingleSelectByIndex, search_information, which_item)
341
+ end
342
+
331
343
  def self.table_value_at(search_information, row, column=0)
332
344
  string_from(:Table_ValueAt, search_information, row, column)
333
345
  end
@@ -381,6 +393,13 @@ module RAutomation
381
393
  end
382
394
 
383
395
  private
396
+ def self.integers_from(method, search_information)
397
+ item_count = send method, search_information, nil
398
+ pointer = FFI::MemoryPointer.new :pointer, item_count
399
+ send method, search_information, pointer
400
+ pointer.read_array_of_int(item_count)
401
+ end
402
+
384
403
  def self.strings_from(method, hwnd)
385
404
  string_count = send method, hwnd, nil
386
405
  pointer = FFI::MemoryPointer.new :pointer, string_count
@@ -27,7 +27,7 @@ RAutomation provides:
27
27
  s.require_paths = ["lib"]
28
28
 
29
29
  s.add_runtime_dependency("ffi")
30
- s.add_development_dependency("rspec", "~>2.3")
30
+ s.add_development_dependency("rspec", "~> 2.14")
31
31
  s.add_development_dependency("rake")
32
32
  s.add_development_dependency("yard")
33
33
  end
@@ -15,7 +15,7 @@ describe 'MsUia::SelectList', :if => SpecHelper.adapter == :ms_uia do
15
15
  about.select_list(:id => 'multiFruitListBox')
16
16
  end
17
17
 
18
- it '#fruits_combo' do
18
+ it '#select_list' do
19
19
  fruits_combo.should exist
20
20
 
21
21
  RAutomation::Window.wait_timeout = 0.1
@@ -29,37 +29,43 @@ describe 'MsUia::SelectList', :if => SpecHelper.adapter == :ms_uia do
29
29
  fruits_combo.should exist
30
30
  end
31
31
 
32
- it '#options' do
33
- fruits_combo.options.size.should == 5
32
+ context 'SelectListOption' do
33
+ it '#selected? & #select' do
34
+ fruits_combo.options(:text => 'Apple')[0].should_not be_selected
35
+ fruits_combo.options(:text => 'Apple')[0].select.should be_true
36
+ fruits_combo.options(:text => 'Apple')[0].should be_selected
37
+ end
34
38
 
35
- expected_options = ['Apple', 'Caimito', 'Coconut', 'Orange', 'Passion Fruit']
36
- fruits_combo.options.map {|option| option.text}.should == expected_options
37
- end
39
+ it '#value' do
38
40
 
39
- it '#selected? & #select' do
40
- fruits_combo.options(:text => 'Apple')[0].should_not be_selected
41
- fruits_combo.options(:text => 'Apple')[0].select.should be_true
42
- fruits_combo.options(:text => 'Apple')[0].should be_selected
43
- end
41
+ #default empty state
42
+ fruits_combo.value.should == ''
44
43
 
45
- it '#value' do
44
+ fruits_combo.options(:text => 'Apple')[0].select
45
+ fruits_combo.value.should == 'Apple'
46
46
 
47
- #default empty state
48
- fruits_combo.value.should == ''
47
+ fruits_combo.options(:text => 'Caimito')[0].select
48
+ fruits_combo.value.should == 'Caimito'
49
+ end
49
50
 
50
- fruits_combo.options(:text => 'Apple')[0].select
51
- fruits_combo.value.should == 'Apple'
51
+ it 'enabled/disabled' do
52
+ fruits_combo.should be_enabled
53
+ fruits_combo.should_not be_disabled
52
54
 
53
- fruits_combo.options(:text => 'Caimito')[0].select
54
- fruits_combo.value.should == 'Caimito'
55
+ disabled_combo.should_not be_enabled
56
+ disabled_combo.should be_disabled
57
+ end
55
58
  end
56
59
 
57
- it 'enabled/disabled' do
58
- fruits_combo.should be_enabled
59
- fruits_combo.should_not be_disabled
60
+ it '#select' do
61
+ multi_fruits.select text: /ng/
62
+ multi_fruits.values.should eq(['Orange', 'Mango'])
63
+ end
60
64
 
61
- disabled_combo.should_not be_enabled
62
- disabled_combo.should be_disabled
65
+ it '#clear' do
66
+ multi_fruits.select # select all
67
+ multi_fruits.clear text: 'Orange'
68
+ multi_fruits.values.should eq(['Apple', 'Mango'])
63
69
  end
64
70
 
65
71
  it '#values' do
@@ -73,10 +79,19 @@ describe 'MsUia::SelectList', :if => SpecHelper.adapter == :ms_uia do
73
79
 
74
80
  it '#option' do
75
81
  fruits_combo.option(:text => 'Apple').should_not be_selected
76
- fruits_combo.option(:text => 'Apple').set
82
+ fruits_combo.option(:index => 0).set
77
83
  fruits_combo.option(:text => 'Apple').should be_selected
78
84
  end
79
85
 
86
+ it '#options' do
87
+ fruits_combo.options.size.should == 5
88
+ fruits_combo.options.map(&:text).should eq(['Apple', 'Caimito', 'Coconut', 'Orange', 'Passion Fruit'])
89
+
90
+ fruits_combo.options(text: 'Apple').map(&:text).should eq ['Apple']
91
+ fruits_combo.options(text: /Ap{2}le/).map(&:text).should eq ['Apple']
92
+ fruits_combo.options(index: 0).map(&:text).should eq ['Apple']
93
+ end
94
+
80
95
  it 'cannot select anything on a disabled select list' do
81
96
  expect { disabled_combo.option(:text => 'Apple').set }.to raise_error
82
97
  end
@@ -1,12 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "MsUia::Table", :if => SpecHelper.adapter == :ms_uia do
3
+ describe 'MsUia::Table', :if => SpecHelper.adapter == :ms_uia do
4
+ let(:window) { RAutomation::Window.new(:title => "MainFormWindow") }
4
5
  let(:data_entry) { RAutomation::Window.new(:title => "DataEntryForm") }
6
+ let(:data_grid_view) { window.button(value: 'Data Grid View').click {true}; RAutomation::Window.new(title: /DataGridView/) }
7
+ let(:large_grid) { data_entry.close; data_grid_view.table(id: 'dataGridView1') }
5
8
  let(:table) { data_entry.table(:id => "personListView") }
6
9
  let(:toggle_multi_select) { data_entry.button(:value => 'Toggle Multi').click { true } }
7
10
 
8
11
  before :each do
9
- window = RAutomation::Window.new(:title => "MainFormWindow")
10
12
  window.button(:value => "Data Entry Form").click { RAutomation::Window.new(:title => "DataEntryForm").exists? }
11
13
  end
12
14
 
@@ -42,11 +44,42 @@ describe "MsUia::Table", :if => SpecHelper.adapter == :ms_uia do
42
44
  table.row_count.should eq(2)
43
45
  end
44
46
 
47
+ it '#select' do
48
+ large_grid.select value: /^FirstName[1-9]$/
49
+ first_nine = large_grid.rows.take(9)
50
+ expect(first_nine.count).to be 9
51
+ first_nine.should be_all(&:selected?)
52
+ end
53
+
54
+ it '#clear' do
55
+ first_three = large_grid.rows.take(3)
56
+ next_six = large_grid.rows.take_while { |r| r.index.between?(3, 9) }
57
+ large_grid.select value: /^FirstName[1-9]$/
58
+
59
+ large_grid.clear value: /^FirstName[1-3]$/
60
+
61
+ first_three.all?(&:selected?).should be_false
62
+ next_six.all?(&:selected?).should be_true
63
+ end
64
+
65
+ it '#selected_rows' do
66
+ large_grid.select value: /^FirstName[1-5]$/
67
+ large_grid.selected_rows.map(&:index).should eq([0, 1, 2, 3, 4])
68
+ end
69
+
45
70
  context "#rows" do
46
71
  it "has rows" do
47
72
  table.rows.size.should eq 2
48
73
  end
49
74
 
75
+ it 'are quick to find' do
76
+ large_grid.row_count.should eq(51)
77
+
78
+ start = Time.now
79
+ large_grid.row(index: 50).should exist
80
+ (Time.now - start).should be < 1.5
81
+ end
82
+
50
83
  it "have values" do
51
84
  table.rows.map(&:value).should eq ["John Doe", "Anna Doe"]
52
85
  end
@@ -3,8 +3,6 @@ require "spec_helper"
3
3
  describe "MsUia::Window", :if => SpecHelper.adapter == :ms_uia do
4
4
  let(:window) {RAutomation::Window.new(:title => /MainFormWindow/i)}
5
5
 
6
-
7
-
8
6
  it "move and click" do
9
7
  #window = RAutomation::Window.new(:title => /MainFormWindow/i)
10
8
  window.maximize
@@ -12,7 +10,6 @@ describe "MsUia::Window", :if => SpecHelper.adapter == :ms_uia do
12
10
  sleep 1
13
11
  window.click_mouse
14
12
  sleep 1
15
-
16
13
  end
17
14
 
18
15
  context "#send_keys" do
@@ -130,14 +130,20 @@ describe RAutomation::Window do
130
130
  to_not raise_exception
131
131
  end
132
132
 
133
- it "#child", :if => [:win_32, :ms_uia].include?(SpecHelper.adapter) do
134
- window = RAutomation::Window.new(:title => SpecHelper::DATA[:window1_full_title])
135
- window.should exist
136
-
137
- # buttons are windows too. so let's find the button for now
138
- child = window.child(:title => /About/i)
139
- child.should exist
140
- child.title.should == "&About"
141
- child.adapter.should == SpecHelper.adapter
133
+ context '#child', :if => [:win_32, :ms_uia].include?(SpecHelper.adapter) do
134
+ let(:window) { RAutomation::Window.new(:title => SpecHelper::DATA[:window1_full_title]) }
135
+
136
+ it 'immediate children' do
137
+ # buttons are windows too. so let's find the button for now
138
+ child = window.child(:title => '&About')
139
+ child.should exist
140
+ child.title.should == "&About"
141
+ child.adapter.should == SpecHelper.adapter
142
+ end
143
+
144
+ it 'popups' do
145
+ window.button(:title => '&About').click { true }
146
+ window.child(:title => 'About').should be_visible
147
+ end
142
148
  end
143
149
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rautomation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarmo Pertman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-03 00:00:00.000000000 Z
11
+ date: 2014-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '2.3'
33
+ version: '2.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '2.3'
40
+ version: '2.14'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +119,8 @@ files:
119
119
  - ext/UiaDll/RAutomation.UIA/Properties/AutomationProperties.cs
120
120
  - ext/UiaDll/RAutomation.UIA/RAutomation.UIA.csproj
121
121
  - ext/UiaDll/UiaDll.sln
122
+ - ext/UiaDll/UiaDll/ArrayHelper.cpp
123
+ - ext/UiaDll/UiaDll/ArrayHelper.h
122
124
  - ext/UiaDll/UiaDll/AssemblyInfo.cpp
123
125
  - ext/UiaDll/UiaDll/ControlMethods.cpp
124
126
  - ext/UiaDll/UiaDll/DynamicAssemblyResolver.cpp
@@ -149,6 +151,9 @@ files:
149
151
  - ext/UiaDll/UiaDll/stdafx.cpp
150
152
  - ext/UiaDll/UiaDll/stdafx.h
151
153
  - ext/UiaDll/UiaDll/targetver.h
154
+ - ext/WindowsForms/.nuget/NuGet.Config
155
+ - ext/WindowsForms/.nuget/NuGet.exe
156
+ - ext/WindowsForms/.nuget/NuGet.targets
152
157
  - ext/WindowsForms/WindowsForms.sln
153
158
  - ext/WindowsForms/WindowsForms/AboutBox.Designer.cs
154
159
  - ext/WindowsForms/WindowsForms/AboutBox.cs
@@ -178,6 +183,7 @@ files:
178
183
  - ext/WindowsForms/WindowsForms/UIA.Extensions.dll
179
184
  - ext/WindowsForms/WindowsForms/ValueMonthCalendar.cs
180
185
  - ext/WindowsForms/WindowsForms/WindowsForms.csproj
186
+ - ext/WindowsForms/WindowsForms/packages.config
181
187
  - lib/rautomation.rb
182
188
  - lib/rautomation/adapter/autoit.rb
183
189
  - lib/rautomation/adapter/autoit/button.rb