die Qual der Wahl mit Selenium 1

Posted by fwoeck
on Tuesday, May 26

Wie bringe ich meinem Integrationstest – also hier letztlich Selenium – bei, mehrere Optionen eines Multiselect-Feldes auszuwählen? Die vom SeleniumDriver angebotene Methode select sieht so aus:

select(selectLocator,optionLocator)

wobei der optionLocator soweit ich es sehe nur eine(1) Option zulässt. Das mehrmalige Aufrufen nacheinander klappt leider auch nicht, weil die vorher angewählte Option beim neuen Schritt wieder deaktiviert wird.

Dazu ist mir nur eingefallen, den Wert mit JavaScript zu setzen. Hier benutze ich jQuery, weil es in der Website eh benutzt wird. Mit “run_script” lassen sich Skripte unter dem Driver ausführen. In einer Cucumber-Stepsdefinition sieht das dann so aus:

Given /^I choose "([^\"]*)" and "([^\"]*)" from "([^\"]*)"$/ do |opt1, opt2, field|
  selenium.run_script("$('##{field}').val(['#{opt1}','#{opt2}'])")
end

Der val-Methode von jQuery kann man ein Wertearray übergeben.

Weblinks

  1. selenium.rubyforge.org/rdoc/classes/Selenium
  2. jquery-select-elements-tips-and-tricks

dynamische Select-Menüs mit JavaScript 0

Posted by fwoeck
on Tuesday, November 04

Wenn man Ajax umgehen möchte, um die Auswahl eines bestimmten Select-Feldes von der Auswahl eines anderen abhängig zu machen, kann man diese Funktion auch mit JavaScript allein auf der Clientseite regeln.

Ein javascript-Controller

Wir legen einen Controller javascript an. Der Trick an der Sache besteht darin, dass wenn der Client später eine URL /javascript/...js aufruft und eine entsprechende Route gesetzt ist, der neue Controller aufgerufen wird und dynamisch ein js-File erzeugen kann.

Hier befüllt der Controller das @jobs-Array, das später mit dem js-Skript auf die einzelnen Customer-Bereiche aufgeteilt wird:

class JavascriptsController < ApplicationController
  def dynamic_jobs
    @jobs = Job.all_cached
  end
end

Die Route dorthin

müsste dann so lauten:

map.connect ':controller/:action.:format'

Der View-Abschnitt

In diesem Falle sollen innerhalb eines Formulars für das Project-Modell die möglichen Jobs von der Auswahl des Customers abhängen:

<% javascript 'dynamic_jobs' %>
...
<% form_for @project do |f| %>
  ...
<p>
  Kunde<br />
  <%= f.collection_select :customer_id, Customer.all_cached, :id, :name, :prompt => "" %>
</p>

<p id="job_field">
  Job<br />
  <%= f.collection_select :job_id, Job.all_cached, :id, :name, :prompt => "" %>
</p>

Das File dynamic_jobs.js.erb

befindet sich im views/javascripts-Ordner. Es erzeugt das js-File mit den Kunden und Jobs. Die Funktion customerSelect teilt die Daten des Arrays in Kunden ein.

var jobs = new Array();
<% for job in @jobs -%>
  jobs.push(new Array(<%= job.customer_id %>, '<%=h job.name %>', <%= job.id %>));
<% end -%>

function customerSelected() {
  customer_id = $('project_customer_id').getValue();
  options = $('project_job_id').options;
  options.length = 1;
  jobs.each(function(job) {
    if (job[0] == customer_id) {
      options[options.length] = new Option(job[1], job[2]);
    }
  });
  if (options.length == 1) {
    $('job_field').hide();
  } else {
    $('job_field').show();
  }
}

document.observe('dom:loaded', function() {
  customerSelected();
  $('project_customer_id').observe('change', customerSelected);
});

und schließlich

benötigen wir noch einen Helper, der das dynamisch erzeugt js-File in das Rendering einbaut – z.B. in der application_helper.rb:

def javascript(*args)
  args = args.map { |arg| arg == :defaults ? arg : arg.to_s }
  content_for(:head) { javascript_include_tag(*args) }
end

Deutsche Namen für Datums-select-Helfer 0

Posted by fwoeck
on Sunday, October 12

... konfiguriert man so:

<%#= date_select :guest, :alt_date, :start_year => Time.now.year, :end_year => 
      1.year.from_now.year %>

<%= select_day Time.now.day, :prefix => 'guest', :field_name => "alt_date(3i)" %>

<%= select_month Time.now.month, :prefix => 'guest', :field_name => 
      "alt_date(2i)", :use_month_names => %w(Januar Februar März April May Juni Juli 
      August September Oktober November Dezember) %>

<%= select_year Time.now.year, :prefix => 'guest', :field_name => "alt_date(1i)", 
      :start_year => Time.now.year, :end_year => 1.year.from_now.year %>

<%= hidden_field_tag 'guest[alt_date(4i)]', '12' %>

<%= hidden_field_tag 'guest[alt_date(5i)]', '0' %>

Gotcha: Lässt man die beiden hidden_field_tags weg und ist der eigentliche Datenbanktyp datetime so wird die Zeit auf 0:00 UTC gesetzt, was bei der lokalen Zeitzone Berlin dazu führt, dass der Tageszähler um einen vorrutscht.