1 module selenium.workflow; 2 3 import std.stdio; 4 5 import std.conv; 6 import std.exception; 7 import std.traits; 8 import std.meta; 9 import std.algorithm.searching; 10 import std.algorithm.iteration; 11 import std.range; 12 13 import selenium.session; 14 import selenium.api; 15 16 17 import std.string; 18 import std.conv; 19 20 import vibe.core.log; 21 22 abstract class SeleniumPage { 23 protected { 24 immutable SeleniumSession session; 25 } 26 27 this(immutable SeleniumSession session) { 28 this.session = session; 29 } 30 31 abstract bool isPresent(); 32 } 33 34 class WorkflowCheck(T, U) : Workflow!(T, U) { 35 36 this(T child, U cls) { 37 super(child, cls); 38 } 39 40 auto opDispatch(string name, T...)(T props) if(name != "define") { 41 alias member = child.opDispatch!(name, T); 42 43 static if(is(ReturnType!member == bool)) { 44 assert(child.opDispatch!name(props), "Check `" ~ name ~ "` fail."); 45 return this; 46 } else { 47 auto val = child.opDispatch!name(props); 48 49 return new WorkflowCheck!(typeof(val), void*)(val, null); 50 } 51 } 52 } 53 54 class WorkflowNamed(string workflowName, T ,U) : Workflow!(T, U) { 55 56 this(T child, U cls) { 57 super(child, cls); 58 } 59 60 auto opDispatch(string name, T...)(T props) if(name != "define" && name != "hasStep") { 61 static if(workflowName == name) { 62 return new Workflow!(typeof(this), U)(this, cls); 63 } else { 64 return super.opDispatch!name(props); 65 } 66 } 67 68 bool hasStep(string name)() { 69 static if(workflowName == name) { 70 return true; 71 } else { 72 return child.hasStep!name; 73 } 74 } 75 } 76 77 class Workflow(T, U) { 78 immutable SeleniumSession session; 79 T child; 80 U cls; 81 82 this(immutable SeleniumSession session) { 83 this.session = session; 84 } 85 86 static if(!is(T == void*)) { 87 this(T child, U cls) { 88 this(child.session); 89 90 this.child = child; 91 this.cls = cls; 92 } 93 } 94 95 bool hasStep(string name)() { 96 static if(!is(U == void*) && __traits(hasMember, cls, name)) { 97 return true; 98 } else static if(!is(T == void*)) { 99 return child.hasStep!name; 100 } else { 101 return false; 102 } 103 } 104 105 auto check()() { 106 static if(is(T == void*)) { 107 assert(false, "Can not check void workflows."); 108 } else static if(!__traits(isSame, TemplateOf!(T), WorkflowCheck)) { 109 return new WorkflowCheck!(typeof(this), void*)(this, null); 110 } else { 111 assert(false, "Can not check this workflow."); 112 } 113 } 114 115 private void logDispatch(string name, T...)(T props) { 116 string stringParams = ""; 117 118 static if(T.length == 0) { 119 //logInfo("=> " ~ name); 120 } else { 121 foreach(prop; props) { 122 stringParams ~= " " ~ prop.to!string; 123 } 124 125 //logInfo("=> " ~ name ~ ":" ~ stringParams); 126 } 127 } 128 129 private auto callClassMember(string name, T...)(T props) { 130 alias expectedParam = Parameters!(__traits(getMember, cls, name)); 131 132 enum diffParameters = expectedParam.length - props.length; 133 134 static assert(diffParameters <= 2, "Can not call `" ~ name ~ "` due invalid number of parameters"); 135 136 static if(diffParameters) { 137 assert(is(expectedParam[0] == typeof(session)) || is(expectedParam[0] == typeof(this)), 138 "First parameter expected of type `immutable SeleniumSession` or `Workflow`"); 139 140 static if(is(expectedParam[0] == typeof(session))) { 141 alias prepend = AliasSeq!(session); 142 } else { 143 alias prepend = AliasSeq!(this); 144 } 145 } else { 146 alias prepend = AliasSeq!(); 147 } 148 149 alias finalProps = AliasSeq!(prepend, props); 150 151 static if(is(ReturnType!(__traits(getMember, cls, name)) == void)) { 152 __traits(getMember, cls, name)(finalProps); 153 return this; 154 } else { 155 return __traits(getMember, cls, name)(finalProps); 156 } 157 } 158 159 auto opDispatch(string name, T...)(T props) if(name != "define" && name != "hasStep") { 160 enforce(hasStep!name, "The step `" ~ name ~ "` is undefined."); 161 162 logDispatch!name(props); 163 164 static assert(!is(U == void*) && !is(T == void*), "Can not call method `" ~ name ~ "` on `void` child and class."); 165 166 enum classHasMember = __traits(hasMember, cls, name); 167 168 static if(classHasMember) { 169 return callClassMember!name(props); 170 } else static if(!is(T == void*)) { 171 return child.opDispatch!name(props); 172 } 173 } 174 } 175 176 auto define(immutable SeleniumSession session) { 177 return new Workflow!(void*, void*)(session); 178 } 179 180 auto define(string name, T, U)(T workflow, U obj) { 181 return new WorkflowNamed!(name, T, U)(workflow, obj); 182 } 183 184 auto define(T, U)(T workflow, U obj) { 185 return new Workflow!(T, U)(workflow, obj); 186 } 187 188 class WebNavigation { 189 void goTo(immutable SeleniumSession session, string url) { 190 session.navigation.url = url; 191 } 192 } 193 194 struct HistoryCue(T) { 195 alias E = ReturnType!T; 196 197 private { 198 immutable SeleniumSession session; 199 T callback; 200 E result; 201 202 ulong index = 0; 203 204 string expectedUrl; 205 } 206 207 this(immutable SeleniumSession session, T callback) { 208 this.session = session; 209 this.callback = callback; 210 211 this.expectedUrl = session.navigation.url; 212 this.result = callback(session, index); 213 } 214 215 E front() { 216 return result; 217 } 218 219 E moveFront() { 220 return result; 221 } 222 223 void popFront() { 224 index++; 225 226 while(this.expectedUrl != session.navigation.url) { 227 session.navigation.back; 228 } 229 230 result = callback(session, index); 231 } 232 233 bool empty() { 234 return result is null; 235 } 236 } 237 238 auto historyCue(T)(immutable SeleniumSession session, T callback) { 239 return HistoryCue!T(session, callback); 240 } 241 242 void isTrue(bool value, string message = "The value is not `true`") { 243 assert(value, message); 244 } 245 246 void isFalse(bool value, string message = "The value is not `false`") { 247 assert(!value, message); 248 }