Best Howitzer_ruby code snippet using Actions.highlight
button.rb
Source:button.rb
...18 attr_accessor :font_color19 attr_accessor :font_hover_color20 attr_accessor :game21 attr_reader :height22 attr_accessor :highlight_color23 attr_accessor :hover_color24 attr_accessor :highlight_hover_color25 attr_reader :hover_image26 attr_reader :hover_image_height27 attr_reader :hover_image_width28 attr_reader :image29 attr_accessor :image_angle30 attr_accessor :image_background_color31 attr_accessor :image_background_hover_color32 attr_reader :image_height33 attr_accessor :image_position_x34 attr_accessor :image_position_y35 attr_reader :image_width36 attr_reader :image_x37 attr_reader :image_y38 attr_accessor :maximum_font_size39 attr_reader :text40 attr_accessor :text_angle41 attr_accessor :text_position_x42 attr_accessor :text_position_y43 attr_accessor :text_relative_position_x44 attr_accessor :text_relative_position_y45 attr_reader :text_relative_width46 attr_reader :text_x47 attr_reader :text_y48 attr_reader :width49 attr_reader :x50 attr_reader :y51 attr_reader :z52 def initialize(53 actions:,54 color: DEFAULT_COLOR,55 font_color: DEFAULT_TEXT_COLOR,56 game:,57 height: DEFAULT_HEIGHT,58 hover_color: DEFAULT_HOVER_COLOR,59 image_angle: 0,60 image_position_x: 0.5,61 image_position_y: 0.5,62 text_angle: 0,63 text_position_x: 0.5,64 text_position_y: 0.5,65 text_relative_position_x: 0.5,66 text_relative_position_y: 0.5,67 text_relative_width: 1,68 width: DEFAULT_WIDTH,69 x: 0,70 y: 0,71 z: ZOrder::MAIN_UI,72 **options73 )74 self.game = game75 self.actions = actions76 self.border_color = options[:border_color]77 self.border_hover_color = options[:border_hover_color]78 self.border_width = options[:border_width]79 self.color = color80 self.deadzones = options[:deadzones]81 self.font = options[:font]82 self.font_color = font_color83 self.font_hover_color = options[:font_hover_color] || font_color84 self.height = height85 self.highlight_color = options[:highlight_color]86 self.hover_color = hover_color87 self.highlight_hover_color = options[:highlight_hover_color]88 @hover_image = options[:hover_image]89 self.hover_image_height = options[:hover_image_height]90 self.hover_image_width ||= options[:hover_image_width]91 @image = options[:image]92 self.image_angle = image_angle93 self.image_background_color = options[:image_background_color]94 self.image_background_hover_color = options[:image_background_color]95 self.image_height = options[:image_height]96 self.image_position_x = image_position_x97 self.image_position_y = image_position_y98 self.image_width ||= options[:image_width]99 self.text_angle = text_angle100 self.text_position_x = text_position_x101 self.text_position_y = text_position_y102 self.text_relative_position_x = text_relative_position_x103 self.text_relative_position_y = text_relative_position_y104 self.text_relative_width = text_relative_width105 self.width = width106 @hover_image_height ||= @image_height107 @hover_image_width ||= @image_width108 update_coordinates(x: x, y: y, z: z)109 self.text = options[:text]110 end111 %i[height width].each do |attribute|112 define_method(:"#{attribute}=") do |value|113 instance_variable_set(:"@#{attribute}", value&.round)114 self.text = text115 update_coordinates116 send(attribute)117 end118 end119 %i[120 image_position_x121 image_position_y122 text_position_x123 text_position_y124 text_relative_width125 ].each do |attribute|126 define_method(:"#{attribute}=") do |value|127 instance_variable_set(:"@#{attribute}", value)128 self.text = text129 update_coordinates130 send(attribute)131 end132 end133 def actions=(value)134 @actions = game.format_actions(value)135 end136 def draw(mouse_x = nil, mouse_y = nil)137 if mouse_x && mouse_y && within?(mouse_x, mouse_y)138 draw_shape(hover: true)139 draw_image_background(hover: true) if image_background_hover_color140 hover_image&.draw_rot(141 angle: image_angle,142 draw_height: hover_image_height,143 draw_width: hover_image_width,144 x: image_x,145 y: image_y,146 z: z147 )148 image&.tick149 if text150 font&.draw_text(151 text,152 angle: text_angle,153 color: font_hover_color,154 rel_x: text_relative_position_x,155 rel_y: text_relative_position_y,156 x: text_x,157 y: text_y,158 z: z159 )160 end161 draw_highlight(hover: true) if highlight_hover_color162 else163 draw_shape164 draw_image_background if image_background_color165 image&.draw_rot(166 angle: image_angle,167 draw_height: image_height,168 draw_width: image_width,169 x: image_x,170 y: image_y,171 z: z172 )173 hover_image&.tick174 if text175 font&.draw_text(176 text,177 angle: text_angle,178 color: font_color,179 rel_x: text_relative_position_x,180 rel_y: text_relative_position_y,181 x: text_x,182 y: text_y,183 z: z184 )185 end186 draw_highlight if highlight_color187 end188 end189 def font=(value)190 @font = value191 self.maximum_font_size = font&.height192 font193 end194 def hover_image=(value)195 @hover_image = value196 if hover_image_height197 self.hover_image_height = hover_image_height unless hover_image_width198 elsif hover_image_width199 self.hover_image_width = hover_image_width200 end201 hover_image202 end203 def hover_image_height=(value)204 @hover_image_height = value&.round205 @hover_image_width ||= hover_image.width * (hover_image_height / hover_image.height.to_f) if206 hover_image && hover_image_height207 hover_image_height208 end209 def hover_image_width=(value)210 @hover_image_width = value&.round211 @hover_image_height ||= hover_image.height * (hover_image_width / hover_image.width.to_f) if212 hover_image && hover_image_width213 hover_image_width214 end215 def image=(value)216 @image = value217 if image_height218 self.image_height = image_height unless image_width219 elsif image_width220 self.image_width = image_width221 end222 image223 end224 def image_height=(value)225 @image_height = value&.round226 @image_width ||= (image.width * (image_height / image.height.to_f)).round if image &&227 image_height228 image_height229 end230 def image_width=(value)231 @image_width = value&.round232 @image_height ||= (image.height * (image_width / image.width.to_f)).round if image &&233 image_width234 image_width235 end236 def maximize_images_in_square(size)237 if image238 self.image_height = self.image_width = nil239 if image.height > image.width240 self.image_height = size241 else242 self.image_width = size243 end244 end245 return unless hover_image246 self.hover_image_height = self.hover_image_width = nil247 if hover_image.height > hover_image.width248 self.hover_image_height = size249 else250 self.hover_image_width = size251 end252 end253 def perform_actions254 game.execute_actions(actions)255 end256 def perform_image_animation(animation_type, animation_args)257 animation_args = animation_args.merge(x: image_x, y: image_y, z: z)258 hover_image&.perform_animation(259 animation_type,260 draw_height: hover_image_height,261 draw_width: hover_image_width,262 **animation_args263 )264 image&.perform_animation(265 animation_type,266 draw_height: image_height,267 draw_width: image_width,268 **animation_args269 )270 end271 def text=(value)272 @text = value273 return text unless font && maximum_font_size && width && text_relative_width274 # Find maximum font size (between set maximum and minimum) usable while still fitting275 # text on the button. Try skipping the binary search by checking the maximum276 # size first as this is the most common case.277 font_size = maximum_font_size if278 Gosu::Font.new(maximum_font_size).text_width(text) < (width * text_relative_width) - 5279 font_size ||= (MINIMUM_FONT_SIZE...maximum_font_size)280 .to_a281 .reverse282 .bsearch do |size|283 Gosu::Font.new(size).text_width(text) < (width * text_relative_width) - 5284 end285 @font = Gosu::Font.new(font_size || MINIMUM_FONT_SIZE, name: font.name)286 # If text doesn't fit on button even at minimum font size, truncate287 # text with ellipses beyond what can fit at minimum font size288 # unless font_size289 @text = font.truncate_text(text: text, trailing_text: '...', width: width - 20) unless290 font_size291 text292 end293 def update_coordinates(x: nil, y: nil, z: nil)294 @x = x if x295 @y = y if y296 @z = z if z297 @center_x = width && @x ? @x + (width / 2.0) : nil298 @center_y = height && @y ? @y + (height / 2.0) : nil299 @image_x = width && @x && image_position_x ? @x + (width * image_position_x) : nil300 @image_y = height && @y && image_position_y ? @y + (height * image_position_y) : nil301 @text_x = width && @x && text_position_x ? @x + (width * text_position_x) : nil302 @text_y = height && @y && text_position_y ? @y + (height * text_position_y) : nil303 end304 def within?(_x, _y)305 return false if deadzones && within_deadzones?(_x, _y)306 border = border_width || 0307 _x >= x - border && _x < (x + width + border) && _y >= y - border &&308 _y < (y + height + border)309 end310 protected311 def draw_highlight(hover: false)312 color_to_draw = hover ? highlight_hover_color : highlight_color313 Gosu.draw_rect(color: color_to_draw, height: height, width: width, x: x, y: y, z: z)314 end315 def draw_image_background(hover: false)316 color_to_draw, drawn_height, drawn_width =317 if hover318 [image_background_hover_color, hover_image_height, hover_image_width]319 else320 [image_background_color, image_height, image_width]321 end322 Gosu.draw_rect(323 color: color_to_draw,324 from_center: true,325 height: drawn_height,326 width: drawn_width,327 x: image_x,328 y: image_y,329 z: z330 )331 end332 def draw_shape(hover: false)333 color_to_draw, border_color_to_draw =334 hover ? [hover_color, border_hover_color] : [color, border_color]335 if border_color_to_draw336 Gosu.draw_rect(337 color: border_color_to_draw,338 height: height + border_width,339 width: width + border_width,340 x: x - border_width,341 y: y - border_width,342 z: z343 )344 end345 return unless color_to_draw346 Gosu.draw_rect(color: color_to_draw, height: height, width: width, x: x, y: y, z: z)347 end348 def within_deadzones?(_x, _y)349 deadzones.each do |deadzone|350 return true if _x >= deadzone[:x] && _x < deadzone[:x] + deadzone[:width] &&351 _y >= deadzone[:y] && _y < deadzone[:y] + deadzone[:height]352 end353 false354 end355 end356 class CircularButton < Button357 attr_accessor :border_circle358 attr_accessor :border_hover_circle359 attr_accessor :circle360 attr_accessor :highlight_circle361 attr_accessor :hover_circle362 attr_accessor :hover_highlight_circle363 attr_reader :radius364 def initialize(365 actions:,366 color: DEFAULT_COLOR,367 font_color: DEFAULT_TEXT_COLOR,368 game:,369 hover_color: DEFAULT_HOVER_COLOR,370 image_angle: 0,371 image_position_x: 0.5,372 image_position_y: 0.5,373 radius:,374 text_angle: 0,375 text_position_x: 0.5,376 text_position_y: 0.5,377 text_relative_position_x: 0.5,378 text_relative_position_y: 0.5,379 x: 0,380 y: 0,381 z: ZOrder::MAIN_UI,382 **options383 )384 self.game = game385 self.radius = radius386 self.border_width = options[:border_width]387 self.actions = actions388 self.border_color = options[:border_color]389 self.border_hover_color = options[:border_hover_color]390 self.color = color391 self.deadzones = options[:deadzones]392 self.font = options[:font]393 self.font_color = font_color394 self.font_hover_color = options[:font_hover_color] || font_color395 self.highlight_color = options[:highlight_color]396 self.highlight_hover_color = options[:highlight_hover_color]397 self.hover_color = hover_color398 @hover_image = options[:hover_image]399 self.hover_image_height = options[:hover_image_height]400 self.hover_image_width ||= options[:hover_image_width]401 @image = options[:image]402 self.image_angle = image_angle403 self.image_background_color = options[:image_background_color]404 self.image_background_hover_color = options[:image_background_hover_color]405 self.image_height = options[:image_height]406 self.image_position_x = image_position_x407 self.image_position_y = image_position_y408 self.image_width ||= options[:image_width]409 self.text_angle = text_angle410 self.text_position_x = text_position_x411 self.text_position_y = text_position_y412 self.text_relative_position_x = text_relative_position_x413 self.text_relative_position_y = text_relative_position_y414 @hover_image_height ||= @image_height415 @hover_image_width ||= @image_width416 update_coordinates(x: x, y: y, z: z)417 self.text = options[:text]418 end419 def border_color=(value)420 @border_color = value421 if border_color422 self.border_circle =423 Image.new(Gosu::Circle.new(color: border_color, radius: radius + border_width))424 end425 border_color426 end427 def border_hover_color=(value)428 @border_hover_color = value429 if border_hover_color430 self.border_hover_circle = Image.new(431 Gosu::Circle.new(color: border_hover_color, radius: radius + border_width)432 )433 end434 border_hover_color435 end436 def border_width=(value)437 @border_width = value&.round438 self.border_color = border_color439 self.border_hover_color = border_hover_color440 self.highlight_color = highlight_color441 self.highlight_hover_color = highlight_hover_color442 border_width443 end444 def color=(value)445 @color = value446 self.circle =447 if color448 Image.new(Gosu::Circle.new(color: color, radius: radius))449 else450 nil451 end452 color453 end454 def highlight_color=(value)455 @highlight_color = value456 self.highlight_circle = Image.new(Gosu::Circle.new(color: highlight_color, radius: radius)) if457 highlight_color458 highlight_color459 end460 def highlight_hover_color=(value)461 @highlight_hover_color = value462 if highlight_hover_color463 self.hover_highlight_circle =464 Image.new(Gosu::Circle.new(color: highlight_hover_color, radius: radius))465 end466 highlight_hover_color467 end468 def hover_color=(value)469 @hover_color = value470 self.hover_circle = Image.new(Gosu::Circle.new(color: hover_color, radius: radius)) if471 hover_color472 hover_color473 end474 def radius=(value)475 @radius = value.round476 self.height = self.width = radius * 2477 self.color = color478 self.highlight_color = highlight_color479 self.highlight_hover_color = highlight_hover_color480 self.hover_color = hover_color481 self.text = text482 radius483 end484 def update_coordinates(x: nil, y: nil, z: nil)485 @x = @center_x = x if x486 @y = @center_y = y if y487 @z = z if z488 @image_x =489 radius && @x && image_position_x ? @x - radius + (radius * image_position_x * 2) : nil490 @image_y =491 radius && @y && image_position_y ? @y - radius + (radius * image_position_y * 2) : nil492 @text_x = radius && @x && text_position_x ? @x - radius + (radius * text_position_x * 2) : nil493 @text_y = radius && @y && text_position_y ? @y - radius + (radius * text_position_y * 2) : nil494 end495 def within?(_x, _y)496 return false if deadzones && within_deadzones?(_x, _y)497 Math.sqrt((_x - x).abs**2 + (_y - y).abs**2) < (radius + (border_width || 0))498 end499 protected500 def draw_highlight(hover: false)501 circle_to_draw = hover ? hover_highlight_circle : highlight_circle502 circle_to_draw&.draw(from_center: true, x: center_x, y: center_y, z: z)503 end504 def draw_shape(hover: false)505 if hover506 border_hover_circle&.draw(from_center: true, x: center_x, y: center_y, z: z)507 hover_circle&.draw(from_center: true, x: center_x, y: center_y, z: z)508 else509 border_circle&.draw(from_center: true, x: center_x, y: center_y, z: z)510 circle&.draw(from_center: true, x: center_x, y: center_y, z: z)511 end512 end513 end514end...
core.rb
Source:core.rb
...55 @@sti_create_links = true56 # prefix messages with current timestamp, set the format to display (you can use I18n keys) or true and :short will be used57 cattr_accessor :timestamped_messages58 @@timestamped_messages = false59 # a hash of string (or array of strings) and highlighter string to highlight words in messages. It will use highlight rails helper60 cattr_accessor :highlight_messages61 @@highlight_messages = nil62 # instance-level configuration63 # ----------------------------64 # provides read/write access to the local Actions DataStructure65 attr_reader :actions66 def actions=(args)67 @actions = ActiveScaffold::DataStructures::Actions.new(*args)68 end69 # provides read/write access to the local Columns DataStructure70 attr_reader :columns71 def columns=(val)72 @columns._inheritable = val.collect {|c| c.to_sym}73 # Add virtual columns74 @columns << val.collect {|c| c.to_sym unless @columns[c.to_sym]}.compact75 end76 # lets you override the global ActiveScaffold frontend for a specific controller77 attr_accessor :frontend78 # lets you override the global ActiveScaffold theme for a specific controller79 attr_accessor :theme80 # enable caching of action link urls81 attr_accessor :cache_action_link_urls82 # lets you specify whether add a create link for each sti child for a specific controller83 attr_accessor :sti_create_links84 def add_sti_create_links?85 self.sti_create_links and not self.sti_children.nil?86 end87 # action links are used by actions to tie together. they appear as links for each record, or general links for the ActiveScaffold.88 attr_reader :action_links89 # a generally-applicable name for this ActiveScaffold ... will be used for generating page/section headers90 attr_writer :label91 def label(options={})92 as_(@label, options) || model.model_name.human(options.merge(options[:count].to_i == 1 ? {} : {:default => model.name.pluralize}))93 end94 # STI children models, use an array of model names95 attr_accessor :sti_children96 # prefix messages with current timestamp, set the format to display (you can use I18n keys) or true and :short will be used97 attr_accessor :timestamped_messages98 # a hash of string (or array of strings) and highlighter string to highlight words in messages. It will use highlight rails helper99 attr_accessor :highlight_messages100 ##101 ## internal usage only below this point102 ## ------------------------------------103 def initialize(model_id)104 # model_id is the only absolutely required configuration value. it is also not publicly accessible.105 @model_id = model_id106 # inherit the actions list directly from the global level107 @actions = self.class.actions.clone108 # create a new default columns datastructure, since it doesn't make sense before now109 attribute_names = self.model.columns.collect{ |c| c.name.to_sym }.sort_by { |c| c.to_s }110 association_column_names = self.model.reflect_on_all_associations.collect{ |a| a.name.to_sym }.sort_by { |c| c.to_s }111 @columns = ActiveScaffold::DataStructures::Columns.new(self.model, attribute_names + association_column_names)112 # and then, let's remove some columns from the inheritable set.113 @columns.exclude(*self.class.ignore_columns)114 @columns.exclude(*@columns.find_all { |c| c.column and (c.column.primary or c.column.name =~ /(_id|_count)$/) }.collect {|c| c.name})115 @columns.exclude(*self.model.reflect_on_all_associations.collect{|a| :"#{a.name}_type" if a.options[:polymorphic]}.compact)116 # inherit the global frontend117 @frontend = self.class.frontend118 @theme = self.class.theme119 @cache_action_link_urls = self.class.cache_action_link_urls120 @sti_create_links = self.class.sti_create_links121 # inherit from the global set of action links122 @action_links = self.class.action_links.clone123 @timestamped_messages = self.class.timestamped_messages124 @highlight_messages = self.class.highlight_messages125 end126 # To be called after your finished configuration127 def _load_action_columns128 #ActiveScaffold::DataStructures::ActionColumns.class_eval {include ActiveScaffold::DataStructures::ActionColumns::AfterConfiguration}129 # then, register the column objects130 self.actions.each do |action_name|131 action = self.send(action_name)132 action.columns.set_columns(self.columns) if action.respond_to?(:columns)133 end134 end135 # To be called after your finished configuration136 def _configure_sti137 column = self.model.inheritance_column138 if sti_create_links...
admin.rb
Source:admin.rb
1# frozen_string_literal: true2module TohsakaBot3 module Commands4 module Admin5 require_relative '../../../events/highlight/highlight_core' # for convertpins6 extend Discordrb::Commands::CommandContainer7 command(:registerslash,8 aliases: TohsakaBot.get_command_aliases('commands.tool.admin.register_slash.aliases'),9 description: I18n.t(:'commands.tool.admin.register_slash.description'),10 usage: I18n.t(:'commands.tool.admin.register_slash.usage'),11 permission_level: TohsakaBot.permissions.roles["owner"],12 min_args: 1) do |event, *types|13 command = CommandLogic::RegisterSlash.new(event, types)14 event.respond(command.run[:content])15 end16 command(:eval,17 description: I18n.t(:'commands.tool.admin.eval.description'),18 usage: I18n.t(:'commands.tool.admin.eval.usage'),19 help_available: false,20 permission_level: TohsakaBot.permissions.roles["owner"]) do |event|21 # Hard coded to allow ONLY the owner to have access.22 break unless event.user.id == AUTH.owner_id.to_i23 command = CommandLogic::Eval.new(event, event.message.content[5..])24 event.respond(command.run[:content])25 end26 command(:tester,27 aliases: %i[test t],28 description: 'Command for testing stuff',29 usage: 'test') do |event|30 event.<< event.message.content31 end32 command(:editpermissions,33 aliases: TohsakaBot.get_command_aliases('commands.tool.admin.edit_permissions.aliases'),34 description: I18n.t(:'commands.tool.admin.edit_permissions.description'),35 usage: I18n.t(:'commands.tool.admin.edit_permissions.usage'),36 min_args: 2,37 permission_level: TohsakaBot.permissions.actions["permissions"]) do |event, user, level|38 command = CommandLogic::EditPermissions.new(event, user, level)39 event.respond(command.run[:content], false, nil, nil, false)40 end41 command(:setstatus,42 aliases: %i[np],43 description: 'Now playing status.',44 usage: 'np <type (playing, watching, listening, competing)> <status>',45 min_args: 2,46 permission_level: TohsakaBot.permissions.actions["set_status"]) do |event, type, *msg|47 msg = msg.join(' ')48 type = "playing" unless %w[playing watching listening streaming competing].include?(type)49 TohsakaBot.status(type, msg)50 cfg = YAML.load_file("cfg/config.yml")51 cfg["status"] = [type, msg]52 File.open('cfg/config.yml', 'w') { |f| f.write cfg.to_yaml }53 event.respond("Status changed to `#{msg.strip_mass_mentions}` as `#{type}`.")54 end55 command(:prune,56 description: 'Prunes between 2 and 100 messages in the current channel.',57 usage: 'prune <amount (2-100)>',58 permission_level: TohsakaBot.permissions.actions["message_prune"]) do |event, amount|59 if /\A\d+\z/.match(amount) && (2..100).include?(amount.to_i)60 event.channel.prune(amount.to_i)61 break62 else63 event.respond('The amount of messages has to be between 2 and 100.')64 end65 end66 command(:typing,67 aliases: %i[type],68 description: 'Starts typing.',69 permission_level: TohsakaBot.permissions.actions["typing_event"],70 usage: 'typing <how long (minutes, default is unlimited)>') do |event, duration|71 if event.channel.pm?72 event.<< 'Not allowed in private messages.'73 break74 end75 TohsakaBot.manage_typing(event.channel, duration)76 break77 end78 command(:convertpins,79 description: 'Copies all pinned messages on the specified channel to database and posts them to the highlight channel',80 usage: 'convertpins <Newest|oldest (starting point)> <channel_id (if empty, current channel will be used)> ',81 permission_level: TohsakaBot.permissions.actions["convert_pins"]) do |event, order, channel_id|82 channel = if channel_id.nil?83 event.channel84 else85 BOT.channel(channel_id.to_i)86 end87 pinned_messages = channel.pins88 pinned_messages.reverse! if !order.nil? && order == 'oldest'89 msg = event.respond('Converting...')90 pinned_messages.each do |m|91 next unless TohsakaBot.db[:highlights].where(msg_id: m.id.to_i).empty?92 highlight_core = HighlightCore.new(m, channel.server.id.to_i, channel.id.to_i)93 highlight_core.store_highlight(highlight_core.send_highlight(channel.server.id))94 sleep(2)95 end96 msg.edit('Done!')97 break98 end99 command(:emojilist,100 aliases: %i[emoji le],101 description: 'List all the çµµæå bot has at its disposal.',102 usage: 'emojis') do |event|103 every_emoji = BOT.emoji104 i = 0105 emoji_names = []106 until i == every_emoji.count107 if event.server != every_emoji[i].server...
highlight
Using AI Code Generation
1Actions::highlight(<<-EOF)2Actions::highlight(<<-EOF)3Actions::highlight(<<-EOF)4Actions::highlight(<<-EOF)5Actions::highlight(<<-EOF)6Actions::highlight(<<-EOF)7Actions::highlight(<<-EOF)8Actions::highlight(<<-EOF)9Actions::highlight(<<-EOF)
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!