[LON-CAPA-cvs] cvs: modules /goltermann/Tests Util.pm config.cfg runTests.pl /goltermann/Tests/t UserRelated.pm selenium_InterfaceTest.pm selenium_ProblemTest.pm /goltermann/Tests/template Template.pm

goltermann goltermann at source.lon-capa.org
Wed Jul 17 08:15:25 EDT 2013


goltermann		Wed Jul 17 12:15:25 2013 EDT

  Added files:                 
    /modules/goltermann/Tests/t	UserRelated.pm 
                               	selenium_InterfaceTest.pm 
                               	selenium_ProblemTest.pm 

  Modified files:              
    /modules/goltermann/Tests	Util.pm config.cfg runTests.pl 
    /modules/goltermann/Tests/template	Template.pm 
  Log:
  added new files
  
  
-------------- next part --------------
Index: modules/goltermann/Tests/Util.pm
diff -u modules/goltermann/Tests/Util.pm:1.1 modules/goltermann/Tests/Util.pm:1.2
--- modules/goltermann/Tests/Util.pm:1.1	Tue Jun 18 09:47:27 2013
+++ modules/goltermann/Tests/Util.pm	Wed Jul 17 12:15:24 2013
@@ -5,16 +5,16 @@
 package Util;
 use Test::More;
 use Test::WWW::Selenium;
-use Smart::Comments;
+# use Smart::Comments;
 
 my $maxTimeout = 30000;
 my $screenCount;            # for screenshot naming
 my $sel;
 my $browser;
-my %user;
 my %config;
 
 # this starts a selenium instance and a browser
+# as parameters you need the string of a browser you want to start and the config hash
 sub startBrowser(){
     ($browser, %config) = @_;
 
@@ -27,7 +27,7 @@
     $sel->open('/');        # if you want to maximize you first have to open a page
     $sel->wait_for_page_to_load($maxTimeout);
     $sel->window_maximize() if $config{'maximize'};
-    # $sel->set_speed(1000);
+    $sel->set_speed(500);
     return $sel;
 }
 
@@ -42,7 +42,7 @@
 
 # Login for loncapa
 sub login(){
-    %user = @_;
+    #%config = @_;
 
     # have a clean start
     logout();
@@ -51,8 +51,8 @@
     $sel->wait_for_page_to_load($maxTimeout);
 
     if($sel->get_title() =~ /login/i){
-        $sel->type("id=uname", $user{'user'});
-        $sel->type('//div[2]/div/form/input[4]', $user{'pw'});
+        $sel->type("id=uname", $config{'user'});
+        $sel->type('//div[2]/div/form/input[4]', $config{'pw'});
         $sel->submit('name=client');
 
     }else{
@@ -117,4 +117,5 @@
     }
     $screenCount += 1;
 }
+
 1;
Index: modules/goltermann/Tests/config.cfg
diff -u modules/goltermann/Tests/config.cfg:1.1 modules/goltermann/Tests/config.cfg:1.2
--- modules/goltermann/Tests/config.cfg:1.1	Tue Jun 18 09:47:27 2013
+++ modules/goltermann/Tests/config.cfg	Wed Jul 17 12:15:24 2013
@@ -22,7 +22,7 @@
 
 # --- Customization ----------------------------------------
 # to speed things up and skip some tests
-fast=1
+fast=0
 
 # maximize the browser window on the test system
 # it is recommended to maximize the window if you are taking screenshots
Index: modules/goltermann/Tests/runTests.pl
diff -u modules/goltermann/Tests/runTests.pl:1.1 modules/goltermann/Tests/runTests.pl:1.2
--- modules/goltermann/Tests/runTests.pl:1.1	Tue Jun 18 09:47:27 2013
+++ modules/goltermann/Tests/runTests.pl	Wed Jul 17 12:15:24 2013
@@ -2,128 +2,227 @@
 use strict;
 use warnings;
 use Test::More 0.98;
+use Array::Utils qw(:all);
 use Smart::Comments;
-use lib('/home/httpd/lib/perl','.','./t');
+use lib( '/home/httpd/lib/perl', '.', './t' );
+
+print "\nLON-CAPA testing script v0.27-Alpha\nPlease read documentation.\n\n";
 
 # -- LONCAPA Testsuite --
-# This will run several automated tests for the lonnet module and tests lon capa in different browsers.
-# During certains tests screenshots will be captured. These Screenshots are stored where your selenium server is located.
-# You can deactivate this feature in the config file
+#
+# The normal way to run this script is via make. But it is also possible to run it as a stand alone script.
+# this enables you to pass parameters which override some of the standard settings below.
+# You can use them to run a single testmodule and/or browser instead of the whole suite.
+# In case you want to repeat a specific test scenario to observe the behavior.
+#
+# use 'make test' to run the whole suite or use standalone mode
+# for standalone type 'perl runTests.pl' plus parameters:
+#
+# perl runTests.pl [lonnet modulename]
+#   OR
+# perl runTests.pl <browser module> <browser>
+# if you want to run browser tests in standalone mode you have to give the browsers you want to run the tests in
+#
+# Examples:
+# perl runTests.pl firefox                  # only tests with firefox
+# perl runTests.pl UserRelated              # only the module 'UserRelated' (you dont need the 'lonnet_' prefix)
+# perl runTests.pl safari InterfaceTest     # module 'InterfaceTest' tested with safari
+#
+#
+#
+# During certain selenium tests screenshots will be captured.
+# These Screenshots are stored where your selenium server is located.
+# You can deactivate this feature in the config file.
 #
 # If you want to write your own test module you have to give your module an explicit package name.
 # Also the module needs a run() function which contains all the tests included in your module.
 # It is recommended to start with the Template.pm to create a your own testmodule!
 # Dont forget to add it to the module list below!
+
+
+# --- START OF CONFiGURATiON -----------------------------------------------------------------------
 #
-# --- START of CONFiGURATiON -----------------------------------------------------------------------
-# package names of modules you want to test:
-my @internalTestModules = ('UserRelated');                  # internal tests
-my @browserTestModules = ('ProblemTest','InterfaceTest');   # browser tests
+# Specify the path on your host machine for browsers which cannot be found by selenium.
+# You might need to add pathes for other browser, depending on the system you are running
 #
-# specify path on host machine for browser selenium server cannot find
-# you sometimes might need to add pathes for other browser
-my $pathToChrome='C:\Program Files\Google\Chrome\Application\chrome.exe';
+# example:
+# my $pathToChrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe';
+# my @browsers = ("googlechrome $pathToChrome");
+
+# browsers you want to test:
+my @browsers = ('firefox', 'googlechrome', 'safari', 'iexplore' );
 #
-# order of the browsers you want to test
-my @browsers=('firefox',"googlechrome $pathToChrome",'safari','iexplore');
-# my @browsers=('safari');
+# --- END OF CONFiGURATiON ------------------------------------------------------------------------
 
-my %config;
-loadConfig();
-# --- END of CONFiGURATiON ------------------------------------------------------------------------
+my %config = loadConfig();# load the configuration file
 
-# TODO: cvs commit
+my @seleniumTestModules;  # for browser tests (need @pecial treatment)
+my @otherTestModules;     # all the other tests
+generateTestLists();      # gets all the test modules from ./t and sorts them in one of the lists above
 
-print "\nLON-CAPA testing script v0.25-Alpha\nPlease read documentation.\n\n";
+my @parameters = @ARGV;
+handleParameters();       # gets given parameters and decides how to handle them (standalone only)
+
+# starting the clock
+my $startTime = time();
 
-my $selenium_unavailable = 0;
 checkPrerequisites();
+runInternalTests();
+runSeleniumTests();
 
-if($selenium_unavailable){
-     print "No Selenium tests possible. Continue anyway? [y/n]\n";
-        if (<STDIN> =~ 'n') {
-            die 'Aborted by user';
+# thats it, end of script
+
+# -------------------------------------------------------------------------------------------------
+
+# loads the configuration from the config file
+sub loadConfig {
+    my @toHash;
+    my %ret;
+    open FILE, "<config.cfg" or die $!;
+
+    while (<FILE>) {
+        if ( $_ =~ /^[a-z]/ ) {
+            @toHash = split( /=/, $_ );
+            $toHash[1] =~ s/\s//g;
+            $ret{ $toHash[0] } = $toHash[1];
+
+            # print $toHash[0]."=>".$config{$toHash[0]};
         }
+    }
+
+    close FILE;
+    return %ret;
 }
 
-# starting the clock
-my $startTime = time();
 
-runInternalTests();
-runBrowserTests() unless $selenium_unavailable; # to spare you all the error messages
+# reads all the tests from t/ and generates the list of tests to run
+sub generateTestLists {
 
-# end of script
-sub checkPrerequisites {
+    my $directory = './t';
+    opendir (DIR, $directory) or die $!;
 
+    while (my $file = readdir(DIR)) {
+
+        if ($file =~ m/^selenium_\w+\.pm$/){
+            $file =~ s/\.pm//;          # remove file extension
+            push(@seleniumTestModules, $file);
+        }elsif($file =~ m/\.pm$/){
+            $file =~ s/\.pm//;          # remove file extension
+            push(@otherTestModules, $file);
+        }
+    }
+    closedir(DIR);
+}
+
+# gets all the given paramters (standalone mode) and decides what to do
+sub handleParameters {
+    return if not @parameters;
+
+    my @allPossibleArguments = (@browsers, @seleniumTestModules, @otherTestModules);
+    my @validGivenArguments = &generateIntersection(\@parameters, \@allPossibleArguments);
+
+    if( scalar @validGivenArguments != scalar @parameters ){
+        print "\nInvalid arguments. Valid arguments are:\n";
+        foreach(@browsers){
+            print "$_\n";
+        }
+        foreach(@seleniumTestModules, @otherTestModules){
+            my $modName = $_;
+            print "$modName\n";
+        }
+        die;
+    }
+
+    # process valid arguments
+    @browsers = intersect(@validGivenArguments, @browsers);
+    @seleniumTestModules = intersect(@validGivenArguments, @seleniumTestModules);
+    @otherTestModules = intersect(@validGivenArguments, @otherTestModules);
+}
+
+sub checkPrerequisites {
+    my $seleniumError = '';
     subtest "internal prerequisites" => sub {
 
         use_ok('Apache::lonnet');
         use_ok('Apache::Tests::Util');
-        # checking if test user is valid
-        ok(&Apache::lonnet::authenticate($config{'user'},$config{'pw'},$config{'domain'}) ne 'no_host', 'test user existing');
 
-    } or die 'Error. there is no use in running more tests';
+        # checking if test user is valid
+        ok(
+            &Apache::lonnet::authenticate( $config{'user'}, $config{'pw'}, $config{'domain'} )
+            ne 'no_host', 'test user existing'
+        );
+    } or die 'Error. There is no use in running more tests';
 
     subtest "external prerequisites" => sub {
 
         use_ok('WWW::Selenium');
         use_ok('Test::WWW::Selenium');
 
-    } or $selenium_unavailable = 1;
+    }
 }
-
 sub runInternalTests {
-    foreach my $module (@internalTestModules){
+    foreach my $module (@otherTestModules) {
         print "\nTesting $module\n";
         subtest "module $module" => sub {
             use_ok($module);
             $module->run(%config);
+            done_testing();
         };
-    }# end of foreach modules
+    }    # end of foreach modules
 }
 
-sub runBrowserTests {
+sub runSeleniumTests {
     my $sel;
     print "Some tests will be skiped due to fast mode\n" if $config{'fast'};
-    foreach my $browser (@browsers){
-        print "\nstarting $browser\n";
-        $sel = &Util::startBrowser($browser, %config);
+    foreach my $browser (@browsers) {
+
+        print "\n    starting $browser\n";
+        $sel = &Util::startBrowser( $browser, %config );
+        subtest 'testrun in ' . $browser => sub {
 
-        subtest 'testrun in '.$browser => sub {
+            foreach my $module (@seleniumTestModules) {
 
-            foreach my $module (@browserTestModules){
-                subtest 'module '.$module => sub {
+                subtest 'module ' . $module => sub {
                     use_ok($module);
                     $module->run(%config);
-                }
+
+                    done_testing();
+                    }
             }
+            done_testing();
         };
         &Util::endBrowser();
     }
     $sel->stop($sel) if $sel;
 }
 
-sub loadConfig {
-    my @toHash;
-    open FILE, "<config.cfg" or die $!;
-    while(<FILE>){
-        if( $_ =~ /^[a-z]/){
-            @toHash = split(/=/,$_);
-            $toHash[1] =~ s/\s//g;
-            $config{$toHash[0]} = "$toHash[1]";
-           # print $toHash[0]."=>".$config{$toHash[0]};
+# generate intersection of two arrays
+sub generateIntersection {
+    my ($a1, $a2) = @_;
+    my @result = ();
+
+    foreach my $a (@$a1){
+        foreach my $b (@$a2){
+
+            if ($a eq $b){
+                push (@result, $a);
+                last;
+            }
+
         }
     }
-    close FILE;
+
+    return @result;
 }
 
-END{
+# done testing, time measurement, the end
+END {
     done_testing();
-
-    my $testtime = time()-$startTime;
-    my $h = ($testtime/60/60) % 24;
-    my $m = ($testtime/60) % 60;
-    my $s = $testtime % 60;
+    $startTime = time() unless $startTime;
+    my $testtime = time() - $startTime;
+    my $h        = ( $testtime / 60 / 60 ) % 24;
+    my $m        = ( $testtime / 60 ) % 60;
+    my $s        = $testtime % 60;
 
     print "Testing finished after $h h $m m $s s\n";
 }
Index: modules/goltermann/Tests/template/Template.pm
diff -u modules/goltermann/Tests/template/Template.pm:1.1 modules/goltermann/Tests/template/Template.pm:1.2
--- modules/goltermann/Tests/template/Template.pm:1.1	Tue Jun 18 09:47:27 2013
+++ modules/goltermann/Tests/template/Template.pm	Wed Jul 17 12:15:24 2013
@@ -6,19 +6,20 @@
 
 
 
-# optional
-BEGIN{
-    # e.g. getting running selenium instance # OMG nein! bad example, causes bugs! dont do it
-    # but using BEGIN blocks might be useful for some other things
-}
+# This is a template you can/should use to create your own test modules. Important things are:
+# You have to give your module a unique packagename which is similar to its filename, without the file extension
+# Your module _needs_ a run() subfunction, which is called by the runTests script
+# If the module is a selenium test, the file and package names have to start with "selenium_"
+
 
 
-# mandatory!
+# run method is mandatory!
 sub run(){
-    shift; # you need this if you pass a hash between different packages when using $module->run();
-    my($sel, %config) = @_;
+    shift; # you need to dispose the first element when using $module->run();
+    my %config = @_;
 
     # call your testfunctions here
+    myTestFunction();
 
     # please logout after inBrowser testing, leave it tidy for the person (/test) after you
 }
@@ -33,4 +34,4 @@
     # e.g. logout
 }
 
-1;
\ No newline at end of file
+1;

Index: modules/goltermann/Tests/t/UserRelated.pm
+++ modules/goltermann/Tests/t/UserRelated.pm
#! perl
use strict;
use warnings;
package UserRelated;
use Test::More;
# use Smart::Comments;
my $user;
my $pw;
my $tmppw;
my $domain;
my $homeserver;

sub run(){
    shift;
    my %config = @_;
    $user = $config{'user'};
    $pw = $config{'pw'};
    $tmppw = 'tmppw12345';           # if something breaks while changing the pw (it shouldnt) you might need the temp password to set your old pw back manually
    $domain = $config{'domain'};


    # Trying to write a testhash
    is(&Apache::lonnet::appenv(%config), 'ok', 'writing %config in appenv');
    is(&Apache::lonnet::delenv(%config), 'ok', 'deleting %config in appenv');
    # i dont think this is a good 'clean' test

    testHomeserver();
    testPwChange();
    testIdChange();
    newTest();
}

sub testHomeserver(){

    # trying to determine hostserver of user
    $homeserver = &Apache::lonnet::homeserver($user,$domain);
    ok($homeserver ne 'no_host', 'host found '.$homeserver);
    ok($homeserver ne 'no_account_on_host', 'account found');
}

sub testPwChange(){

    # changes the password and see if it works
    isnt(&Apache::lonnet::changepass($user, $domain, 'somebadpwwhichshouldnotwork', $homeserver), 'ok', 'trying wrong pw');
    is(&Apache::lonnet::changepass($user,$domain,$pw,'tmppw12345',$homeserver), 'ok', 'change pw');
    isnt(&Apache::lonnet::authenticate($user,$tmppw,$domain), 'no_host', 'authenticate with new pw');
    is(&Apache::lonnet::changepass($user,$domain,$tmppw,$pw,$homeserver), 'ok', 'changing to old pw');
}

sub testIdChange() {

    # overrides old id without asking. thats ok when using a specially created testuser, but
    # DONT USE WITH A PRODUCTIVE USER
    my $uniqueId = localtime;
    &Apache::lonnet::idput($domain, ($user => $uniqueId));
    # checks if ID successfully set
    my %idrget = &Apache::lonnet::idrget($domain, ($user));
    ok($idrget{$user} eq $uniqueId, 'set new id');
}

sub newTest {
     print &Apache::lonnet::queryauthenticate($user,$domain);
}


END{
    # TODO: test if repair function is needed
    # if (errors){
    #     repair
    # }
}

1;

Index: modules/goltermann/Tests/t/selenium_InterfaceTest.pm
+++ modules/goltermann/Tests/t/selenium_InterfaceTest.pm
#!perl
use strict;
use warnings;
package selenium_InterfaceTest;
# use Smart::Comments;
use Test::More;
use Test::WWW::Selenium;


my $maxTimeout = 30000;
my $sel;


# for now this only sends a internal msg to yourself and checks if this arrived
sub run(){
    shift; # _important_ to have the right hash to work with
    my %config = @_;

    &Util::login(%config);
    &sendmessage(%config);
}

# send and recieve of internal messages
sub sendmessage{

    $sel = Util::getActiveBrowser();
    my %config = @_;
    my $user = $config{'user'};

    my $address = $sel->{'browser_url'};        # url stem from selenium instance
    my $timestamp = time;
    my $uniqueText = 'Dies ist eine Testnachricht.';

    # send a mail
    $sel->open($address.'adm/email?compose=individual');
    $sel->wait_for_page_to_load($maxTimeout);
    $sel->type('recuname', $user);
    $sel->type('subject', $timestamp);
    $sel->type('message', $uniqueText);
    &Util::takeScreenshot('send mail interface');
    $sel->click_ok('send', undef, 'mail sent');
    $sel->pause(2000);

    # check for new mail and get value
    my $htmlsource = $sel->get_html_source();
    ok($htmlsource =~ m/<a href="\/adm\/email\?display=(.*?)&.*?">$timestamp/i, 'mail recieved');
    my $clickme = $1;

    &Util::takeScreenshot('recieved mails');

    # delete mail with saved value
    $sel->click_ok("name=delmark value=$clickme", undef, 'checkbox for mail set');
    $sel->click_ok("name=go", undef, 'mail deleted');
    $sel->wait_for_page_to_load($maxTimeout);
    $sel->pause(5000);
}
1;

Index: modules/goltermann/Tests/t/selenium_ProblemTest.pm
+++ modules/goltermann/Tests/t/selenium_ProblemTest.pm
#!perl
use strict;
use warnings;
package selenium_ProblemTest;
use Test::More;
use Test::WWW::Selenium;
# use Smart::Comments;

my $problemname = 'uniqueTestProb.problem';
my $maxTimeout = 30000;
my $oldConstrSpace = 1;              #set this if you use the old construction space (<version_2_11_X)

my $sel;
my %config;
my $user;
my $pass;
my $dom;

# runs all tests
sub run(){
    shift; # get rid of foreign package name, _important_ to have the right hash to work with
    %config = @_;

    $sel = Util::getActiveBrowser();

    # makes code easier to read and tidier
    $user = $config{'user'};
    $pass = $config{'pw'};
    $dom = $config{'domain'};

    # start your test functions here:
    &testLogins();
    &createProblem();

    SKIP: {
        skip "This test might fail because LONCAPA times out randomly", 2;
        &testMaxima();
    };

    &cleanup();
}

sub testLogins(){

    &badLogin();

    &Util::login(%config);
    ok($sel->get_html_source() !~ /href="\/adm\/loginproblems.html"/, 'Login');
}

# Creates an empty problem
# Contains 3 tests
sub createProblem(){

# check if previous login was ok

    $sel->open('/adm/roles');
    $sel->wait_for_page_to_load($maxTimeout);
    &Util::takeScreenshot('available roles');
    $sel->click("//input[\@name='au".$dom."']", undef, 'rolechoice author');
    $sel->wait_for_page_to_load($maxTimeout);

    if($oldConstrSpace){
        $sel->open("/priv/$user/");
    }else{
        $sel->open("/priv/$dom/$user/");
    }
    $sel->wait_for_page_to_load($maxTimeout);
    &Util::takeScreenshot('construction space');
    $sel->select('css=select[name="action"]', 'value=newproblemfile');
    $sel->type('name=newfilename', $problemname);

    # Test1: create .problem file:
    $sel->click_ok('//span/input[3]', undef, 'creating problem file');
    $sel->wait_for_page_to_load($maxTimeout);
    &Util::takeScreenshot('problem file creation');

    # Test 2: confirm problem creation
    if($sel->get_html_source() =~ m/LC_warning/){
        # if test problem already exists
        # print "Bail out! $problemname already exists. cancel to prevent data loss." unless $override;
        $sel->click_ok('css=input[type="submit"]', undef, "$problemname overwritten");
    }else{
        # else select blank template
        $sel->click('css=input[type="submit"]');
        $sel->wait_for_page_to_load($maxTimeout);

        $sel->check("//input[\@value='/home/httpd/html/res/adm/includes/templates/blank.problem']");
        $sel->click_ok('name=newfile', undef, 'chose blank problem template');
    }

    # $sel->wait_for_page_to_load($maxTimeout);
}

# Inserts the XML of a maxima problem
# Contains 2 tests
sub testMaxima(){

    return if $config{'fast'};
    my $maxproblemxml = &Util::getProblemXML_Maxima();

    # open problem in editor [Test 1]
    if($oldConstrSpace){
        $sel->open_ok("/priv/$user/$problemname?forceedit=1", undef, 'open XML editor');
    }else{
        $sel->open_ok("/priv/$dom/$user/$problemname?editmode=Edit&problemmode=editxml", undef, 'open XML editor');
    }
    $sel->wait_for_page_to_load($maxTimeout);

    # Paste problem xml
    $sel->type('name=editxmltext',$maxproblemxml);
    &Util::takeScreenshot('xml editor');

    # [Test 2] finish creating problem
    $sel->click('name=subsaveview');
    $sel->wait_for_page_to_load($maxTimeout);
    # ok if no error message
    &Util::takeScreenshot('finish problem creation');
    unlike($sel->get_html_source(), qr/<span class="LC_error">/, 'create problem');

    # calculate 20 answers, this may take a while
    $sel->click('//div[3]/span/input');
    $sel->pause(1000);

    # waiting time per problem * nr problem * 1000ms:
    $sel->wait_for_page_to_load(2.5*20*1000); #~2.5sec per answer should be fine. if there is a timeout it takes 10sec per timed out answer
}

# Deletes the created problem
# Contains 1 test
sub cleanup(){

    if($oldConstrSpace){
        $sel->open("/priv/$user/$problemname");
    }else{
        $sel->open("/priv/$dom/$user/$problemname");
    }
    $sel->wait_for_page_to_load($maxTimeout);

    # clicking the delete button
    if($oldConstrSpace){
        $sel->click('//li[4]/a/span');
    }else{
        $sel->click('//ul/li[4]/a/span');
    }
    $sel->wait_for_page_to_load($maxTimeout);

    # now delete problem
    $sel->click_ok("css=input[type='submit']", undef, 'cleanup done');
    $sel->wait_for_page_to_load($maxTimeout);
}

# Login with bad pw
# Contains 2 tests
sub badLogin(){
    return if $config{'fast'};
    # page opens
    &Util::logout();
    $sel->open_ok('/adm/roles', undef, 'login page online');
    $sel->wait_for_page_to_load($maxTimeout);
    $sel->pause(1000);    # stabalize safari
    $sel->type('id=uname', $user);

    # alles kaka, einfach timestamp oder so verwenden
    # my $wrongpw = '§wo at 1g3_f5z??w"i#f'; # wrong password
    # # making sure that this is not an actual password...
    # if ($pass ne $wrongpw) {
    #     $sel->type('//div[2]/div/form/input[4]', $wrongpw);
    # }else{
    #     $sel->type('//div[2]/div/form/input[4]', 'somethingdifferent');
    # }
    my $wrongpw = localtime; # this is a wrong password
    $sel->submit('name=client');
    $sel->wait_for_page_to_load($maxTimeout);
    # ok if no link to loginproblems help page
    ok($sel->get_html_source() =~ /href="\/adm\/loginproblems.html"/, 'bad password rejected');
    &Util::takeScreenshot('login failed');
}


1;


More information about the LON-CAPA-cvs mailing list