uia 0.1.2.3 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ === Version 0.1.3 / 2014-04-29
2
+ * Enhancements
3
+ * Element#find can combine multiple locators for filtering on the .NET side
4
+ * #find by :control_type
5
+
1
6
  === Version 0.1.2.3 / 2014-04-21
2
7
  * Bug Fixes
3
8
  * Handles scenario when a Container is not reported from UIA for a SelectionItem element
@@ -142,6 +142,11 @@ namespace UIA.Helper
142
142
  return FindFirst(scope, automationId.IdCondition());
143
143
  }
144
144
 
145
+ public Element ChildWith(TreeScope scope, Condition condition)
146
+ {
147
+ return FindFirst(scope, condition);
148
+ }
149
+
145
150
  public static Element ByName(string name)
146
151
  {
147
152
  return FindFirst(new PropertyCondition(AutomationElement.NameProperty, name));
@@ -0,0 +1,34 @@
1
+ #include "stdafx.h"
2
+ #include <ElementStructures.h>
3
+
4
+ TEST(Conditions_HaveIds)
5
+ {
6
+ auto condition = SearchCondition(123, 0);
7
+ ASSERT_EQ(123, condition.propertyId);
8
+ }
9
+
10
+ TEST(Conditions_CanBeNumbers)
11
+ {
12
+ auto condition = SearchCondition(0, 456);
13
+ ASSERT_EQ(456, condition.number);
14
+ }
15
+
16
+ TEST(Conditions_CanBeStrings)
17
+ {
18
+ auto condition = SearchCondition(0, "expected value");
19
+ ASSERT_STREQ("expected value", condition.string);
20
+ ASSERT_TRUE(condition.IsString());
21
+ }
22
+
23
+ TEST(Conditions_CanHaveMultipleNumbers)
24
+ {
25
+ list<const int> numbers;
26
+ numbers.push_back(7);
27
+ numbers.push_back(6);
28
+
29
+ auto condition = SearchCondition(0, numbers);
30
+ ASSERT_EQ(2, condition.numbersCount);
31
+ ASSERT_EQ(7, condition.numbers[0]);
32
+ ASSERT_EQ(6, condition.numbers[1]);
33
+ ASSERT_TRUE(condition.HasNumbers());
34
+ }
@@ -106,6 +106,7 @@
106
106
  <ClCompile Include="AssemblyInfo.cpp" />
107
107
  <ClCompile Include="ElementInformationTest.cpp" />
108
108
  <ClCompile Include="PatternInformationTest.cpp" />
109
+ <ClCompile Include="SearchConditionTest.cpp" />
109
110
  <ClCompile Include="stdafx.cpp">
110
111
  <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
111
112
  <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
@@ -58,6 +58,9 @@
58
58
  <ClCompile Include="PatternInformationTest.cpp">
59
59
  <Filter>Source Files</Filter>
60
60
  </ClCompile>
61
+ <ClCompile Include="SearchConditionTest.cpp">
62
+ <Filter>Source Files</Filter>
63
+ </ClCompile>
61
64
  </ItemGroup>
62
65
  <ItemGroup>
63
66
  <Image Include="app.ico">
@@ -0,0 +1,57 @@
1
+ #pragma once
2
+ #include "ElementStructures.h"
3
+ #include <list>
4
+
5
+ using namespace System::Collections::Generic;
6
+ using namespace std;
7
+
8
+ ref class ConditionHelper
9
+ {
10
+ public:
11
+ static Condition^ ConditionFrom(list<SearchConditionPtr>& searchConditions) {
12
+ auto conditions = gcnew List<Condition^>();
13
+
14
+ for(auto condition = searchConditions.begin(); condition != searchConditions.end(); ++condition) {
15
+ conditions->Add(ConditionFrom(*condition));
16
+ }
17
+
18
+ if(conditions->Count == 1) {
19
+ conditions->Add(Condition::TrueCondition);
20
+ }
21
+
22
+ return gcnew AndCondition(conditions->ToArray());
23
+ }
24
+
25
+ static Condition^ ConditionFrom(SearchConditionPtr searchCondition) {
26
+ auto automationProperty = AutomationProperty::LookupById(searchCondition->propertyId);
27
+ Object^ value = nullptr;
28
+
29
+ if(automationProperty == AutomationElement::ControlTypeProperty) {
30
+ return ControlTypeConditions(searchCondition->numbers, searchCondition->numbersCount);
31
+ } else if(searchCondition->IsString()) {
32
+ value = gcnew String(searchCondition->string);
33
+ } else {
34
+ value = searchCondition->number;
35
+ }
36
+
37
+ return gcnew PropertyCondition(automationProperty, value);
38
+ }
39
+
40
+ private:
41
+ static Condition^ ControlTypeConditions(const int* controlTypeIds, const int controlTypes) {
42
+ if(controlTypes == 1) {
43
+ return ControlType(controlTypeIds[0]);
44
+ }
45
+
46
+ auto conditions = gcnew List<Condition^>();
47
+ for(auto index = 0; index < controlTypes; ++index) {
48
+ conditions->Add(ControlType(controlTypeIds[index]));
49
+ }
50
+
51
+ return gcnew OrCondition(conditions->ToArray());
52
+ }
53
+
54
+ static Condition^ ControlType(const int id) {
55
+ return gcnew PropertyCondition(AutomationElement::ControlTypeProperty, ControlType::LookupById(id));
56
+ }
57
+ };
@@ -0,0 +1,31 @@
1
+ #include "Stdafx.h"
2
+
3
+ using namespace std;
4
+
5
+ extern "C" {
6
+ __declspec(dllexport) void Condition_Release(SearchConditionPtr searchCondition) {
7
+ delete searchCondition;
8
+ }
9
+
10
+ _declspec(dllexport) SearchConditionPtr Condition_Id(const char* id) {
11
+ return new SearchCondition(AutomationElement::AutomationIdProperty->Id, id);
12
+ }
13
+
14
+ _declspec(dllexport) SearchConditionPtr Condition_Name(const char* name) {
15
+ return new SearchCondition(AutomationElement::NameProperty->Id, name);
16
+ }
17
+
18
+ __declspec(dllexport) SearchConditionPtr Condition_ControlType(const int n, const int arg0, ...) {
19
+ va_list arguments;
20
+ va_start(arguments, arg0);
21
+
22
+ list<const int> controlTypes;
23
+ controlTypes.push_back(arg0);
24
+ for(auto index = 1; index < n; index++) {
25
+ auto value = va_arg(arguments, int);
26
+ controlTypes.push_back(value);
27
+ }
28
+
29
+ return SearchCondition::FromControlTypes(controlTypes);
30
+ }
31
+ }
@@ -1,5 +1,6 @@
1
1
  #include "Stdafx.h"
2
2
 
3
+ #include "ConditionHelper.h"
3
4
  using namespace UIA::Helper;
4
5
 
5
6
  extern "C" {
@@ -31,30 +32,33 @@ extern "C" {
31
32
  }
32
33
  }
33
34
 
34
- __declspec(dllexport) ElementInformationPtr Element_FindById(const char* automationId, char* errorInfo, const int errorLength) {
35
+ ElementInformationPtr ManagedFindByConditions(ElementInformationPtr element, const char* treeScope, list<SearchConditionPtr>& conditions, char* errorInfo, const int errorInfoLength) {
35
36
  try {
36
- return ElementInformation::From(Element::ById(gcnew String(automationId)));
37
- } catch(Exception^ error) {
38
- StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
37
+ auto scope = (TreeScope) Enum::Parse(TreeScope::typeid, gcnew String(treeScope));
38
+ return ElementInformation::From(Find(element)->ChildWith(scope, ConditionHelper::ConditionFrom(conditions)));
39
+ } catch(Exception^ e) {
40
+ StringHelper::CopyToUnmanagedString(e->Message, errorInfo, errorInfoLength);
39
41
  }
40
42
 
41
43
  return NULL;
42
44
  }
43
45
 
44
- __declspec(dllexport) ElementInformationPtr Element_FindChildById(ElementInformationPtr parent, const char* automationId, const char* treeScope, char* errorInfo, const int errorLength) {
45
- try {
46
- auto scope = (TreeScope) Enum::Parse(TreeScope::typeid, gcnew String(treeScope));
47
- return ElementInformation::From(Find(parent)->ChildById(gcnew String(automationId), scope));
48
- } catch(Exception^ error) {
49
- StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
46
+ __declspec(dllexport) ElementInformationPtr FindByConditions(ElementInformationPtr element, const char* treeScope, char* errorInfo, const int errorInfoLength, const int count, SearchConditionPtr arg0, ...) {
47
+ va_list arguments;
48
+ va_start(arguments, arg0);
49
+
50
+ list<SearchConditionPtr> conditions;
51
+ conditions.push_back(arg0);
52
+ for(auto index = 1; index < count; index++) {
53
+ conditions.push_back(va_arg(arguments, SearchConditionPtr));
50
54
  }
51
55
 
52
- return NULL;
56
+ return ManagedFindByConditions(element, treeScope, conditions, errorInfo, errorInfoLength);
53
57
  }
54
58
 
55
- __declspec(dllexport) ElementInformationPtr Element_FindByName(const char* name, char* errorInfo, const int errorLength) {
59
+ __declspec(dllexport) ElementInformationPtr Element_FindById(const char* automationId, char* errorInfo, const int errorLength) {
56
60
  try {
57
- return ElementInformation::From(Element::ByName(gcnew String(name)));
61
+ return ElementInformation::From(Element::ById(gcnew String(automationId)));
58
62
  } catch(Exception^ error) {
59
63
  StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
60
64
  }
@@ -62,10 +66,9 @@ extern "C" {
62
66
  return NULL;
63
67
  }
64
68
 
65
- __declspec(dllexport) ElementInformationPtr Element_FindChildByName(ElementInformationPtr parent, const char* name, const char* treeScope, char* errorInfo, const int errorLength) {
69
+ __declspec(dllexport) ElementInformationPtr Element_FindByName(const char* name, char* errorInfo, const int errorLength) {
66
70
  try {
67
- auto scope = (TreeScope) Enum::Parse(TreeScope::typeid, gcnew String(treeScope));
68
- return ElementInformation::From(Find(parent)->ChildByName(gcnew String(name), scope));
71
+ return ElementInformation::From(Element::ByName(gcnew String(name)));
69
72
  } catch(Exception^ error) {
70
73
  StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
71
74
  }
@@ -2,6 +2,67 @@
2
2
  #include "ArrayHelper.h"
3
3
  #include "StringHelper.h"
4
4
 
5
+ #include <list>
6
+ using namespace std;
7
+
8
+ typedef struct _SearchCondition {
9
+ int propertyId;
10
+
11
+ int number;
12
+ char* string;
13
+ int* numbers;
14
+ int numbersCount;
15
+
16
+ _SearchCondition(const int id, const char* s) : string(NULL), numbers(NULL) {
17
+ Reset(id);
18
+
19
+ auto length = strnlen_s(s, 10000);
20
+ auto size = length + 1;
21
+ this->string = new char[size];
22
+ strcpy_s(this->string, size, s);
23
+ }
24
+
25
+ _SearchCondition(const int id, const int number) : string(NULL), numbers(NULL) {
26
+ Reset(id);
27
+ this->number = number;
28
+ }
29
+
30
+ _SearchCondition(const int id, list<const int>& numbers) : string(NULL), numbers(NULL) {
31
+ Reset(id);
32
+ numbersCount = numbers.size();
33
+ this->numbers = new int[numbersCount];
34
+
35
+ int index = 0;
36
+ for(std::list<const int>::iterator number = numbers.begin(); number != numbers.end(); ++number, ++index) {
37
+ this->numbers[index] = *number;
38
+ }
39
+ }
40
+
41
+ bool HasNumbers() {
42
+ return NULL != numbers;
43
+ }
44
+
45
+ bool IsString() {
46
+ return NULL != string;
47
+ }
48
+
49
+ static _SearchCondition* FromControlTypes(list<const int>& controlTypes) {
50
+ return new SearchCondition(System::Windows::Automation::AutomationElement::ControlTypeProperty->Id, controlTypes);
51
+ }
52
+
53
+ void Reset(const int id) {
54
+ propertyId = id;
55
+ delete[] string; delete[] numbers;
56
+ string = NULL; numbers = NULL;
57
+ number = 0; numbersCount = 0;
58
+ }
59
+
60
+ ~_SearchCondition() {
61
+ Reset(propertyId);
62
+ }
63
+
64
+ } SearchCondition, *SearchConditionPtr;
65
+
5
66
  typedef struct _ElementInformation {
6
67
  int nativeWindowHandle;
7
68
  int* runtimeId;
@@ -13,5 +13,6 @@ using namespace UIA::Helper;
13
13
  #include "ArrayHelper.h"
14
14
  #include "ElementStructures.h"
15
15
  #include "PatternInformationStructures.h"
16
+ #include <list>
16
17
 
17
18
  extern "C" Element^ Find(ElementInformationPtr element);
@@ -80,6 +80,7 @@
80
80
  </ItemGroup>
81
81
  <ItemGroup>
82
82
  <ClInclude Include="ArrayHelper.h" />
83
+ <ClInclude Include="ConditionHelper.h" />
83
84
  <ClInclude Include="DynamicAssemblyResolver.h" />
84
85
  <ClInclude Include="ElementStructures.h" />
85
86
  <ClInclude Include="PatternInformationStructures.h" />
@@ -88,6 +89,7 @@
88
89
  </ItemGroup>
89
90
  <ItemGroup>
90
91
  <ClCompile Include="AssemblyInfo.cpp" />
92
+ <ClCompile Include="ConditionMethods.cpp" />
91
93
  <ClCompile Include="DynamicAssemblyResolver.cpp" />
92
94
  <ClCompile Include="ElementMethods.cpp" />
93
95
  <ClCompile Include="ExpandCollapseMethods.cpp" />
@@ -36,6 +36,9 @@
36
36
  <ClInclude Include="PatternInformationStructures.h">
37
37
  <Filter>Header Files</Filter>
38
38
  </ClInclude>
39
+ <ClInclude Include="ConditionHelper.h">
40
+ <Filter>Header Files</Filter>
41
+ </ClInclude>
39
42
  </ItemGroup>
40
43
  <ItemGroup>
41
44
  <ClCompile Include="UiaDll.cpp">
@@ -89,5 +92,8 @@
89
92
  <ClCompile Include="MouseMethods.cpp">
90
93
  <Filter>Source Files</Filter>
91
94
  </ClCompile>
95
+ <ClCompile Include="ConditionMethods.cpp">
96
+ <Filter>Source Files</Filter>
97
+ </ClCompile>
92
98
  </ItemGroup>
93
99
  </Project>
@@ -9,4 +9,10 @@ class Symbol
9
9
  m.const_get current.split('_').map(&:capitalize).join
10
10
  end
11
11
  end
12
+
13
+ def to_control_type_const
14
+ control_type = Uia::Library::Constants::ControlTypes[self]
15
+ raise InvalidControlType.new(self) unless control_type
16
+ control_type
17
+ end
12
18
  end
data/lib/uia/element.rb CHANGED
@@ -61,9 +61,19 @@ module Uia
61
61
  locator.all? do |locator, value|
62
62
  case locator
63
63
  when :pattern
64
- patterns.include? value
64
+ case value
65
+ when Array
66
+ !(patterns & value).empty?
67
+ else
68
+ patterns.include? value
69
+ end
65
70
  else
66
- send(locator) == value
71
+ case value
72
+ when Array
73
+ value.include? send(locator)
74
+ else
75
+ send(locator) == value
76
+ end
67
77
  end
68
78
  end
69
79
  end
data/lib/uia/finder.rb CHANGED
@@ -29,20 +29,19 @@ module Uia
29
29
  end
30
30
 
31
31
  def find_child(parent, locator)
32
- scope = (locator[:scope] || :descendants).to_s.capitalize
32
+ scope = (locator.delete(:scope) || :descendants).to_s.capitalize
33
+
34
+ valid_locators = [:title, :handle, :id, :name, :value, :control_type, :scope]
35
+ raise BadChildLocator, locator unless (locator.keys - valid_locators).empty?
33
36
 
34
37
  case
35
- when locator[:id]
36
- find_child_by_id parent, locator[:id], scope
37
- when locator[:name], locator[:value]
38
- name_or_value = locator[:name] || locator[:value]
39
- find_child_by_name parent, name_or_value, scope
40
38
  when locator[:title]
41
39
  find_by_title locator[:title], parent.handle
42
40
  when locator[:handle]
43
41
  find_by_handle locator[:handle]
44
42
  else
45
- raise BadChildLocator, locator
43
+ conditions = locator.collect {|k, v| Library.send("#{k}_condition", v) }
44
+ Library.find_by_conditions parent, scope, *conditions
46
45
  end
47
46
  end
48
47
 
@@ -51,18 +50,10 @@ module Uia
51
50
  find_by_property(:id, id)
52
51
  end
53
52
 
54
- def find_child_by_id(parent, id, scope)
55
- Library.find_child_by_id(parent, id, (scope || :descendants).to_s.capitalize)
56
- end
57
-
58
53
  def find_by_name(name)
59
54
  find_by_property(:name, name)
60
55
  end
61
56
 
62
- def find_child_by_name(parent, name, scope)
63
- Library.find_child_by_name(parent, name, (scope || :descendants).to_s.capitalize)
64
- end
65
-
66
57
  def find_by_pid(pid)
67
58
  Library.find_by_pid(pid)
68
59
  end
data/lib/uia/library.rb CHANGED
@@ -63,11 +63,22 @@ module Uia
63
63
  attach_throwable_function :find_by_pid, :Element_FindByProcessId, [:int], ManagedElementStruct.by_ref, &element_or_nil
64
64
  attach_throwable_function :find_by_handle, :Element_FindByHandle, [:int], ManagedElementStruct.by_ref, &element_or_nil
65
65
  attach_function :Element_FindByRuntimeId, [:pointer, :int, :pointer, :int], ManagedElementStruct.by_ref
66
+ attach_function :FindByConditions, [:pointer, :string, :pointer, :int, :int, :varargs], ManagedElementStruct.by_ref
67
+
68
+ ## conditions
69
+ attach_function :release_condition, :Condition_Release, [:pointer], :void
70
+ attach_function :id_condition, :Condition_Id, [:string], SearchCondition.by_ref
71
+ attach_function :name_condition, :Condition_Name, [:string], SearchCondition.by_ref
72
+ self.singleton_class.send(:alias_method, :value_condition, :name_condition)
73
+ attach_function :Condition_ControlType, [:int, :varargs], SearchCondition.by_ref
74
+
75
+ def self.control_type_condition(*control_types)
76
+ args = control_types.flatten.map(&:to_control_type_const).reduce([]) { |a, n| a << :int << n }
77
+ Condition_ControlType control_types.count, *args
78
+ end
66
79
 
67
80
  # element methods
68
81
  attach_throwable_function :send_keys, :Element_SendKeys, [:pointer, :string], :void
69
- attach_throwable_function :find_child_by_id, :Element_FindChildById, [:pointer, :string, :string], ManagedElementStruct.by_ref, &element_or_nil
70
- attach_throwable_function :find_child_by_name, :Element_FindChildByName, [:pointer, :string, :string], ManagedElementStruct.by_ref, &element_or_nil
71
82
  elements_from :children, :Element_Children, [:pointer]
72
83
  elements_from :descendants, :Element_Descendants, [:pointer]
73
84
  attach_throwable_function :click, :Element_ClickClickablePoint, [:pointer], :void
@@ -145,6 +156,15 @@ module Uia
145
156
  result
146
157
  end
147
158
 
159
+ def self.find_by_conditions(element, scope, *args)
160
+ string_buffer = FFI::MemoryPointer.new :char, 1024
161
+ conditions = args.reduce([]) { |a, c| a << :pointer << c }
162
+ result = FindByConditions element, scope, string_buffer, 1024, args.count, *conditions
163
+ error_info = string_buffer.read_string
164
+ raise error_info unless error_info.empty?
165
+ Uia::Element.new(result) unless result.empty?
166
+ end
167
+
148
168
  rescue LoadError => e
149
169
  raise LoadError, 'You must install the Visual Studio 2012 C++ Runtime Environment to use the Uia gem (http://www.microsoft.com/en-us/download/details.aspx?id=30679)'
150
170
  end
@@ -1,4 +1,10 @@
1
1
  module Uia
2
+ class InvalidControlType < StandardError
3
+ def initialize(control_type)
4
+ super "#{control_type} is not valid"
5
+ end
6
+ end
7
+
2
8
  module Library
3
9
  module Constants
4
10
  Patterns = {
@@ -69,5 +69,25 @@ module Uia
69
69
  class ElementStruct < FFI::Struct
70
70
  include ElementLayout
71
71
  end
72
+
73
+ class SearchCondition < FFI::ManagedStruct
74
+ extend StructAttributes
75
+
76
+ layout :property_id, :int,
77
+ :int_value, :int,
78
+ :string_value, :string,
79
+ :numbers, :pointer,
80
+ :numbers_count, :int
81
+
82
+ struct_attr :property_id, :int_value, :string_value
83
+
84
+ def numbers
85
+ self[:numbers].read_array_of_int self[:numbers_count]
86
+ end
87
+
88
+ def self.release(pointer)
89
+ Library.release_condition(pointer)
90
+ end
91
+ end
72
92
  end
73
93
  end
data/lib/uia/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Uia
2
- VERSION = '0.1.2.3'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Symbol do
4
+
5
+ context '#to_control_type_const' do
6
+ Then { expect(:list_item.to_control_type_const).to eq(50007) }
7
+ Then { expect { :not_a_control_type.to_control_type_const }.to raise_error InvalidControlType }
8
+ end
9
+ end
@@ -33,7 +33,7 @@ describe Uia::Element do
33
33
  Then { expect(element.find(id: 'textField')).to be_enabled }
34
34
  Then do
35
35
  expect(element.bounding_rectangle.count).to eq(4)
36
- expect(element.bounding_rectangle).to be_all {|e| e.instance_of? Fixnum }
36
+ expect(element.bounding_rectangle).to be_all { |e| e.instance_of? Fixnum }
37
37
  end
38
38
 
39
39
  context 'visibility' do
@@ -93,7 +93,7 @@ describe Uia::Element do
93
93
  context '#send_keys' do
94
94
  Given(:text_field) { element.find(id: 'textField').as :value }
95
95
  When { text_field.send_keys 'abcde', [:left] * 3, ' fgh ' }
96
- Then { text_field.value == 'ab fgh cde'}
96
+ Then { text_field.value == 'ab fgh cde' }
97
97
  end
98
98
 
99
99
  context '#find' do
@@ -117,6 +117,18 @@ describe Uia::Element do
117
117
  Then { element.find(handle: child_handle).id == 'textField' }
118
118
  end
119
119
 
120
+ context 'control_type' do
121
+ Then { element.find(control_type: :custom).id == 'automatableMonthCalendar1' }
122
+ Then { element.find(control_type: [:custom, :semantic_zoom]).id == 'automatableMonthCalendar1' }
123
+ end
124
+
125
+ context 'combinations' do
126
+ Then { element.find(control_type: :list, name: 'linkLabel1').id == 'FruitListBox' }
127
+ Then { element.find(control_type: :button, name: 'Forward', scope: :children) == nil }
128
+ Then { element.find(control_type: :custom, id: 'automatableMonthCalendar1').name == 'linkLabel1'}
129
+ Then { element.find(value: 'linkLabel1', id: 'automatableMonthCalendar1').control_type == :custom }
130
+ end
131
+
120
132
  context 'invalid' do
121
133
  When(:bad_locator) { element.find(bad_locator: 123) }
122
134
  Then { bad_locator.should have_failed BadLocator, "{:bad_locator=>123} is not a valid locator" }
@@ -127,7 +139,7 @@ describe Uia::Element do
127
139
  end
128
140
  end
129
141
 
130
- context '#select' do
142
+ context '#filter' do
131
143
  context 'control_type' do
132
144
  When(:buttons) { element.filter(control_type: :radio_button) }
133
145
  Then { buttons.map(&:control_type) == [:radio_button] * 3 }
@@ -140,6 +152,14 @@ describe Uia::Element do
140
152
  context 'combinations' do
141
153
  Then { element.filter(control_type: :button, name: 'About')[0].id == 'aboutButton' }
142
154
  end
155
+
156
+ context 'multiple' do
157
+ When(:radio_or_value) { element.filter(control_type: [:radio_button, :text]) }
158
+ Then { expect(radio_or_value.count).to eq(8) }
159
+
160
+ When(:value_or_invoke) { element.filter(pattern: [:value, :text]) }
161
+ Then { expect(value_or_invoke.count).to eq(5) }
162
+ end
143
163
  end
144
164
 
145
165
  context '#click' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2.3
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-22 00:00:00.000000000 Z
12
+ date: 2014-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
@@ -151,6 +151,7 @@ files:
151
151
  - ext/UiaDll/UiaDll.Test/MemoryLeakDetector.h
152
152
  - ext/UiaDll/UiaDll.Test/PatternInformationTest.cpp
153
153
  - ext/UiaDll/UiaDll.Test/ReadMe.txt
154
+ - ext/UiaDll/UiaDll.Test/SearchConditionTest.cpp
154
155
  - ext/UiaDll/UiaDll.Test/StringHelperTest.cpp
155
156
  - ext/UiaDll/UiaDll.Test/UiaDll.Test.cpp
156
157
  - ext/UiaDll/UiaDll.Test/UiaDll.Test.vcxproj
@@ -163,6 +164,8 @@ files:
163
164
  - ext/UiaDll/UiaDll.sln
164
165
  - ext/UiaDll/UiaDll/ArrayHelper.h
165
166
  - ext/UiaDll/UiaDll/AssemblyInfo.cpp
167
+ - ext/UiaDll/UiaDll/ConditionHelper.h
168
+ - ext/UiaDll/UiaDll/ConditionMethods.cpp
166
169
  - ext/UiaDll/UiaDll/DynamicAssemblyResolver.cpp
167
170
  - ext/UiaDll/UiaDll/DynamicAssemblyResolver.h
168
171
  - ext/UiaDll/UiaDll/ElementMethods.cpp
@@ -218,6 +221,7 @@ files:
218
221
  - spec/app/FizzWare.NBuilder.dll
219
222
  - spec/app/UIA.Extensions.dll
220
223
  - spec/app/WindowsForms.exe
224
+ - spec/core_ext/symbol_spec.rb
221
225
  - spec/spec_helper.rb
222
226
  - spec/uia/element_spec.rb
223
227
  - spec/uia/keys_spec.rb
@@ -251,7 +255,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
251
255
  version: '0'
252
256
  segments:
253
257
  - 0
254
- hash: -439398319
258
+ hash: -604577939
255
259
  required_rubygems_version: !ruby/object:Gem::Requirement
256
260
  none: false
257
261
  requirements:
@@ -260,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
260
264
  version: '0'
261
265
  segments:
262
266
  - 0
263
- hash: -439398319
267
+ hash: -604577939
264
268
  requirements: []
265
269
  rubyforge_project:
266
270
  rubygems_version: 1.8.28
@@ -271,6 +275,7 @@ test_files:
271
275
  - spec/app/FizzWare.NBuilder.dll
272
276
  - spec/app/UIA.Extensions.dll
273
277
  - spec/app/WindowsForms.exe
278
+ - spec/core_ext/symbol_spec.rb
274
279
  - spec/spec_helper.rb
275
280
  - spec/uia/element_spec.rb
276
281
  - spec/uia/keys_spec.rb