Browse Source

Implemented basic test suite for forms

master
Artyom Beilis 14 years ago
parent
commit
da3093e51d
5 changed files with 420 additions and 77 deletions
  1. +2
    -0
      form.cpp
  2. +75
    -6
      form.h
  3. +166
    -68
      tests/form_test.cpp
  4. +168
    -0
      tests/form_test.py
  5. +9
    -3
      tests/test_plan.txt

+ 2
- 0
form.cpp View File

@@ -835,6 +835,7 @@ void checkbox::render_value(form_context &context)

void checkbox::load(http::context &context)
{
auto_generate();
std::pair<http::request::form_type::const_iterator,http::request::form_type::const_iterator>
range=context.request().post_or_get().equal_range(name());
value(false);
@@ -1308,6 +1309,7 @@ void submit::render_value(form_context &context)

void submit::load(http::context &context)
{
auto_generate();
pressed_ = context.request().post_or_get().find(name()) != context.request().post_or_get().end();
}



+ 75
- 6
form.h View File

@@ -1001,18 +1001,39 @@ namespace cppcms {
util::hold_ptr<data> d;
};

///
/// This class represent an html checkbox input widget
///
class CPPCMS_API checkbox: public base_html_input {
public:
// Can specify other type like "radio"
///
/// The constructor that allows you to specify other type like "radio"
///
checkbox(std::string const &type);
// Default - checkbox
///
/// Default constructor, type checkbox
///
checkbox();
~checkbox();
virtual ~checkbox();
///
/// Returns true of box was checked (selected)
///
bool value();
///
/// Set checked state
///
void value(bool is_set);

///
/// Get unique identification of the checkbox
///
std::string identification();

///
/// Set unique identification to the checkbox, useful when you want to
/// have many options with same name
///
void identification(std::string const &);

virtual void render_value(form_context &context);
@@ -1023,29 +1044,77 @@ namespace cppcms {
std::string identification_;
bool value_;
};
///
/// Select multiple elements widget
///
class CPPCMS_API select_multiple : public base_widget {
public:
select_multiple();
~select_multiple();

void add(std::string const &string,bool selected=false);
void add(std::string const &string,std::string const &id,bool selected=false);
///
/// Add a new option to list with display name \a msg, and specify if it is initially
/// selected, default false
///
void add(std::string const &msg,bool selected=false);
///
/// Add a new option to list with display name \a msg, and specify if it is initially
/// selected, default false, providing unique identification for the element \a id
///
void add(std::string const &msg,std::string const &id,bool selected=false);
///
/// Add a new option to list with localized display name \a msg, and specify if it is initially
/// selected, default false
///
void add(locale::message const &msg,bool selected=false);
///
/// Add a new option to list with localized display name \a msg, and specify if it is initially
/// selected, default false, providing unique identification for the element \a id
///
void add(locale::message const &msg,std::string const &id,bool selected=false);

///
/// Get the mapping of all selected items according to the order they where added to the list
///
std::vector<bool> selected_map();
///
/// Get all selected items ids according to the order they where added to the list, if no
/// specific id was given, strings "0", "1"... would be used
///
std::set<std::string> selected_ids();

///
/// Get minimal amount of options that should be chosen, default = 0
///
unsigned at_least();

///
/// Set minimal amount of options that should be chosen, default = 0
///
void at_least(unsigned v);

///
/// Get maximal amount of options that should be chosen, default unlimited
///
unsigned at_most();
///
/// Set maximal amount of options that should be chosen, default unlimited
///
void at_most(unsigned v);

///
/// Same as at_least(1)
///
void non_empty();

///
/// Get the number of rows used for widget, default 0 -- undefined
///
unsigned rows();
///
/// Set the number of rows used for widget, default 0 -- undefined
///
void rows(unsigned n);
virtual void render_input(form_context &context);


+ 166
- 68
tests/form_test.cpp View File

@@ -12,6 +12,21 @@ public:
unit_test(cppcms::service &s) : cppcms::application(s)
{
}
void load(cppcms::form &f)
{
if(request().request_method()=="POST" || !request().query_string().empty()) {
response().set_plain_text_header();
std::ostream &out = response().out();
f.load(context());
bool v=f.validate();
if(v) {
out << "valid\n";
}
else {
out << "invalid\n";
}
}
}
virtual void main(std::string a_case)
{
cppcms::widgets::text text;
@@ -28,16 +43,6 @@ public:
cppcms::widgets::radio radio;
cppcms::widgets::submit submit;

cppcms::form X,a,a1,a2,b;
X+text+a+b;
a+a1;
a+a2;
a1+textarea;
a2+integer;
a2+real;
b+p1+p2+regex+email+checkbox+select_multiple;
X+select+radio+submit;


text.message("text");
textarea.message("textarea");
@@ -57,80 +62,173 @@ public:
select_multiple.add("a",true);
select_multiple.add("b",true);
select_multiple.add("c");
select_multiple.add(cppcms::locale::translate("tr1"),std::string("id1"));

select.add("a");
select.add("b");
select.add("c");
select.add(cppcms::locale::translate("tr2"),"id2");
select.selected_id("id2");

radio.add("x");
radio.add("y");
radio.add(cppcms::locale::translate("tr3"),"id3");
radio.selected(0);

cppcms::form X,a,a1,a2,b;

if(a_case == "" )
;// Nothing checked
else if(a_case == "/non_empty") {
text.non_empty();
textarea.non_empty();
integer.non_empty();
real.non_empty();
p1.non_empty();
p2.non_empty();
regex.non_empty();
email.non_empty();
select_multiple.non_empty();
select.non_empty();
radio.non_empty();
}

if(a_case.empty() || a_case == "/non_empty" || a_case=="/sub"){
X+text+a+b;
a+a1;
a+a2;
a1+textarea;
a2+integer;
a2+real;
b+p1+p2+regex+email+checkbox+select_multiple;
X+select+radio+submit;

if(!a_case.empty()) {
text.non_empty();
textarea.non_empty();
integer.non_empty();
real.non_empty();
p1.non_empty();
p2.non_empty();
regex.non_empty();
email.non_empty();
select_multiple.non_empty();
select.non_empty();
radio.non_empty();
}

if(request().request_method()=="POST" || !request().query_string().empty()) {
response().set_plain_text_header();
std::ostream &out = response().out();
X.load(context());
if(X.validate()) {
out << "valid" << std::endl;
if(request().request_method()=="POST" || !request().query_string().empty()) {
response().set_plain_text_header();
std::ostream &out = response().out();
X.load(context());
if(X.validate()) {
out << "valid" << std::endl;
}
else {
out << "invalid" << std::endl;
}
if(text.set()) out << "Text:" << text.value() << std::endl;
if(textarea.set()) out << "Textarea:" << textarea.value() << std::endl;
if(integer.set()) out << "Int:" << integer.value() << std::endl;
if(real.set()) out << "Double:" << real.value() << std::endl;
if(p1.set()) out << "p1:" << p1.value() << std::endl;
if(p2.set()) out << "p2:" << p2.value() << std::endl;
if(regex.set()) out << "regex: " << regex.value() << std::endl;
if(email.set()) out << "email: " << email.value() << std::endl;
if(checkbox.set()) out << "checkbox: " << checkbox.value() << std::endl;
if(select_multiple.set()) {
out << "selected m:";
std::vector<bool> v=select_multiple.selected_map();
for(unsigned i=0;i<v.size();i++)
out << i << " ";
out << std::endl;
std::set<std::string> ids=select_multiple.selected_ids();
out << "selected m ids:";
for(std::set<std::string>::const_iterator p=ids.begin();p!=ids.end();++p)
out << *p << " ";
out<< std::endl;
}
if(select.set())
out << "Select: selected " << select.selected() << " id=" << select.selected_id() << std::endl;
if(radio.set())
out << "Radio: selected " << radio.selected() << " id=" << radio.selected_id() << std::endl;
if(submit.set())
out << "Submit pressed: " << submit.value() << std::endl;

}
else if(a_case !="/sub") {
std::ostream &out = response().out();
out << "non loaded<br>\n";
cppcms::form_context context(out);
out << "<form action=\"" << request().script_name() + request().path_info() << "\" method=\"post\" >\n";
X.render(context);
out << "</form>\n";
}
else {
out << "invalid" << std::endl;
else { // if (a_case=="/sub")
cppcms::form_context context(response().out());
b.render(context);
}
if(text.set()) out << "Text:" << text.value() << std::endl;
if(textarea.set()) out << "Textarea:" << textarea.value() << std::endl;
if(integer.set()) out << "Int:" << integer.value() << std::endl;
if(real.set()) out << "Double:" << real.value() << std::endl;
if(p1.set()) out << "p1:" << p1.value() << std::endl;
if(p2.set()) out << "p2:" << p2.value() << std::endl;
if(regex.set()) out << "regex: " << regex.value() << std::endl;
if(email.set()) out << "email: " << email.value() << std::endl;
if(checkbox.set()) out << "checkbox: " << checkbox.value() << std::endl;
if(select_multiple.set()) {
out << "selected m:";
std::vector<bool> v=select_multiple.selected_map();
for(unsigned i=0;i<v.size();i++)
out << i << " ";
out << std::endl;
std::set<std::string> ids=select_multiple.selected_ids();
out << "selected m ids:";
for(std::set<std::string>::const_iterator p=ids.begin();p!=ids.end();++p)
out << *p << " ";
out<< std::endl;


}
else if(a_case == "/text") {
X+text;
text.limits(2,5);
load(X);
if(text.set()) {
response().out() << text.value();
}
if(select.set())
out << "Select: selected " << select.selected() << " id=" << select.selected_id() << std::endl;
if(radio.set())
out << "Radio: selected " << radio.selected() << " id=" << radio.selected_id() << std::endl;
if(submit.set())
out << "Submit pressed: " << submit.value() << std::endl;

}
else {

}
else if(a_case == "/number") {
X+real;
real.range(5,100);
load(X);
if(real.set()) {
response().out() << real.value();
}

}
else if(a_case == "/pass") {
X+p1+p2;
p1.non_empty();
p2.check_equal(p1);
load(X);
}
else if(a_case == "/checkbox") {
X+checkbox;
load(X);
response().out() << checkbox.value();
}
else if(a_case == "/sm") {
X+select_multiple;
select_multiple.non_empty();
select_multiple.at_most(2);
load(X);
std::ostream &out = response().out();
out << "non loaded<br>\n";
cppcms::form_context context(out);
out << "<form action=\"" << request().script_name() + request().path_info() << "\" method=\"post\" >\n";
std::vector<bool> v=select_multiple.selected_map();
for(unsigned i=0;i<v.size();i++)
out << v[i] << " ";
out << std::endl;
std::set<std::string> ids=select_multiple.selected_ids();
for(std::set<std::string>::const_iterator p=ids.begin();p!=ids.end();++p)
out << *p << " ";
out<< std::endl;
}
else if(a_case == "/select") {
X+select;
select.non_empty();
load(X);
response().out() << select.selected() <<" " << select.selected_id();
}
else if(a_case == "/radio") {
X+radio;
radio.non_empty();
load(X);
response().out() << radio.selected() <<" " << radio.selected_id();
}
else if(a_case == "/submit") {
X+submit;
load(X);
response().out() << submit.value();
}
else if(a_case == "/submitl") {
cppcms::widgets::submit s;
s.id("submit_id");
s.name("submit_name");
s.help(cppcms::locale::translate("help"));
s.error_message(cppcms::locale::translate("error"));
s.message(cppcms::locale::translate("message"));
s.valid(false);
s.value(cppcms::locale::translate("test"));
X+s;
cppcms::form_context context(response().out());
X.render(context);
out << "</form>";
}

}
};



+ 168
- 0
tests/form_test.py View File

@@ -0,0 +1,168 @@
#!/usr/bin/env python
# coding=utf-8
import httplib
import sys

def test(name,A,B):
if A != B:
print "Error :" + name
print "-----Actual--"
print A,"---Expected--"
print B,"-------------"
sys.exit(1)
else:
print "Ok:"+name



h=httplib.HTTPConnection('localhost:8080');
h.request('GET','/test')
r=h.getresponse()
body=r.read();
ref_body = \
"""\
non loaded<br>
<form action="/test" method="post" >
<p>text&nbsp;<span class="cppcms_form_input"><input type="text" name="_1" ></span></p>
<p>textarea&nbsp;<span class="cppcms_form_input"><textarea name="_2" ></textarea></span></p>
<p>int&nbsp;<span class="cppcms_form_input"><input type="text" name="_3" value="" ></span></p>
<p>double&nbsp;<span class="cppcms_form_input"><input type="text" name="_4" value="" ></span></p>
<p>pass&nbsp;<span class="cppcms_form_input"><input type="password" name="_5" ></span></p>
<p>pass2&nbsp;<span class="cppcms_form_input"><input type="password" name="_6" ></span></p>
<p>yes or not&nbsp;<span class="cppcms_form_input"><input type="text" name="_7" ></span></p>
<p>E-Mail&nbsp;<span class="cppcms_form_input"><input type="text" name="_8" ></span></p>
<p>Checkbox&nbsp;<span class="cppcms_form_input"><input type="checkbox" name="_9" value="y" ></span></p>
<p>Select Multiple&nbsp;<span class="cppcms_form_input"><select multiple name="_10" >
<option value="0" selected >a</option>
<option value="1" selected >b</option>
<option value="2" >c</option>
<option value="id1" >tr1</option>
</select></span></p>
<p>Select&nbsp;<span class="cppcms_form_input"><select name="_11" >
<option value="0" >a</option>
<option value="1" >b</option>
<option value="2" >c</option>
<option value="id2" selected >tr2</option>
</select></span></p>
<p>Radio&nbsp;<span class="cppcms_form_input"><div class="cppcms_radio" >
<input type="radio" value="0" name="_12" checked > x<br>
<input type="radio" value="1" name="_12" > y<br>
<input type="radio" value="id3" name="_12" > tr3<br>
</div></span></p>
<p>Submit&nbsp;<span class="cppcms_form_input"><input type="submit" name="_13" value="Button" ></span></p>
</form>
"""

test("/test",body,ref_body)

def test_valid(name,params,ans,url='/non_empty'):
h=httplib.HTTPConnection('localhost:8080');
h.request('GET','/test' + url + '?' + params)
r=h.getresponse()
test(name,r.read()[:len(ans)],ans)

test_valid('non_empty1','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','valid')
test_valid('non_empty2','_1=&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty3','_1=1&_2=&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty4','_1=1&_2=1&_3=&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty5','_1=1&_2=1&_3=1&_4=1&_5=&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty6','_1=1&_2=1&_3=1&_4=1&_5=1&_6=&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty7','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty8','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=&_9=10&_10=1&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty9','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=&_10=1&_11=1&_12=1&_13=1','valid') # checkbox ok
test_valid('non_empty10','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=&_11=1&_12=1&_13=1','invalid')
test_valid('non_empty11','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=&_12=1&_13=1','invalid')
test_valid('non_empty12','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=&_13=1','invalid')
test_valid('non_empty12','_1=1&_2=1&_3=1&_4=1&_5=1&_6=1&_7=yes&_8=a@a&_9=10&_10=1&_11=1&_12=1&_13=','valid') # Submit ok
test_valid('empty','_1=&_2=&_3=&_4=&_5=&_6=&_7=yes&_8=a@a&_9=&_10=&_11=&_12=&_13=','valid','') # Empty ok only regex, email fails
test_valid('empty1','_1=&_2=&_3=&_4=&_5=&_6=&_7=yes&_8=&_9=&_10=&_11=&_12=&_13=','invalid','') # Empty ok only regex, email fails
test_valid('empty2','_1=&_2=&_3=&_4=&_5=&_6=&_7=&_8=a@a&_9=&_10=&_11=&_12=&_13=','invalid','') # Empty ok only regex, email fails


h=httplib.HTTPConnection('localhost:8080');
h.request('GET','/test/sub')
r=h.getresponse()
body=r.read();
ref_body = \
"""\
<p>pass&nbsp;<span class="cppcms_form_input"><input type="password" name="_5" ></span></p>
<p>pass2&nbsp;<span class="cppcms_form_input"><input type="password" name="_6" ></span></p>
<p>yes or not&nbsp;<span class="cppcms_form_input"><input type="text" name="_7" ></span></p>
<p>E-Mail&nbsp;<span class="cppcms_form_input"><input type="text" name="_8" ></span></p>
<p>Checkbox&nbsp;<span class="cppcms_form_input"><input type="checkbox" name="_9" value="y" ></span></p>
<p>Select Multiple&nbsp;<span class="cppcms_form_input"><select multiple name="_10" >
<option value="0" selected >a</option>
<option value="1" selected >b</option>
<option value="2" >c</option>
<option value="id1" >tr1</option>
</select></span></p>
"""
test("subset",body,ref_body)

def test_valid(name,url,params,ans):
def get():
h=httplib.HTTPConnection('localhost:8080');
h.request('GET','/test' + url + '?' + params)
r=h.getresponse()
test(name+' GET',r.read(),ans)
def post():
h=httplib.HTTPConnection('localhost:8080');
headers = {"Content-type": "application/x-www-form-urlencoded"}
h.request('POST','/test' + url,params,headers)
r=h.getresponse()
test(name+' POST',r.read(),ans)
get()
post()


test_valid('text','/text','_1=','invalid\n')
test_valid('text1','/text','_1=x','invalid\nx')
test_valid('text2','/text','_1=xx','valid\nxx')
test_valid('text3','/text','_1=xxxxx','valid\nxxxxx')
test_valid('text4','/text','_1=xxxxxx','invalid\nxxxxxx')
test_valid('text5','/text','_1=%d7%a9%d6%b8%d7%9c%d7%95%d7%9d','valid\nשָלום')
test_valid('text6','/text','_1=%d7%a9%d7%9c','valid\nשל')
test_valid('text7','/text','_1=%FF%FF','invalid\n\xFF\xFF')
test_valid('text8','/text','_1=%01%01','invalid\n\x01\x01')

test_valid('number','/number','_1=','invalid\n')
test_valid('number1','/number','_1=10','valid\n10')
test_valid('number2','/number','_1=10.0','valid\n10')
test_valid('number3','/number','_1=10.0e+','invalid\n')
test_valid('number5','/number','_1=10.0e1','valid\n100')
test_valid('number6','/number','_1=10.0x','invalid\n')
test_valid('number7','/number','_1=A10.0','invalid\n')
test_valid('number8','/number','_1=0','invalid\n0')
test_valid('number9','/number','_1=1000','invalid\n1000')
test_valid('number10','/number','_1=10A','invalid\n')


test_valid('pass1','/pass','_1=&_2=','invalid\n')
test_valid('pass2','/pass','_1=x&_2=x','valid\n')
test_valid('pass3','/pass','_1=x1&_2=x2','invalid\n')

test_valid('checkbox1','/checkbox','_1=n','valid\n0')
test_valid('checkbox2','/checkbox','_1=y','valid\n1')

test_valid('sm1','/sm','foo=bar','invalid\n0 0 0 0 \n\n')
test_valid('sm2','/sm','_1=1&_1=0','valid\n1 1 0 0 \n0 1 \n')
test_valid('sm3','/sm','_1=1&_1=id1','valid\n0 1 0 1 \n1 id1 \n')
test_valid('sm4','/sm','_1=0&_1=1&_1=2','invalid\n1 1 1 0 \n0 1 2 \n')


test_valid('select1','/select','foo=bar','invalid\n-1 ')
test_valid('select2','/select','_1=0','valid\n0 0')
test_valid('select3','/select','_1=0&_1=1','invalid\n-1 ')
test_valid('select4','/select','_1=10','invalid\n-1 ')

test_valid('radio1','/radio','foo=bar','invalid\n-1 ')
test_valid('radio2','/radio','_1=0','valid\n0 0')
test_valid('radio3','/radio','_1=0&_1=1','invalid\n-1 ')
test_valid('radio4','/radio','_1=10','invalid\n-1 ')


test_valid('submit1','/submit','_1=1','valid\n1')
test_valid('submit2','/submit','_2=1','valid\n0')
body='<p><label for="submit_id">message:</label> <span class="cppcms_form_error">error</span> <span class="cppcms_form_input"><input type="submit" id="submit_id" name="submit_name" value="test" ></span><span class="cppcms_form_help">help</span></p>\n'
test_valid('submit3','/submitl','',body)


+ 9
- 3
tests/test_plan.txt View File

@@ -18,11 +18,17 @@
- Cookies: test
- File Server Test
- JSON test
- Forms:
text: encoding-validation
various tests
- Views:
commands
filters
auto-reloading

Done
======
- Forms:
text: encoding-validation
various tests





Loading…
Cancel
Save