Deal with AJAX block in web-crawler or create manually inputs - php

Based on Alvin Bunk article link to article I want to create a web-cralwer that logins in a website then submits a form.
My problem is that on that website there is an Ajax block that generates after clicking and empty link few inputs that I need to fill so I need to click that empty link somehow or to insert the inputs manually .
I changed the code below in a lot of ways to try to make it work but on the visit function I got stuck
I get Uncaught Error: Call to a member function visit() on null
<?php
require 'vendor/autoload.php';
trait MinkSetup
{
private $minkBaseUrl;
private $minkSession;
/**
* #before
*/
public function setupMinkSession()
{
$this->minkBaseUrl = 'https://www.url.com';
$driver = new \Behat\Mink\Driver\Selenium2Driver('firefox');
$this->minkSession = new \Behat\Mink\Session($driver);
$this->minkSession->start();
}
public function getCurrentPage()
{
return $this->minkSession->getPage();
}
public function getCurrentPageContent()
{
return $this->getCurrentPage()->getContent();
}
public function visit($url)
{
echo $url;
$this->minkSession->visit($url);
}
public function login($user, $pass){
$this->minkSession->visit('complete url');
$page = $this->getCurrentPage();
echo $page;
$page->fillField('email', $user); // Enter username.
$page->fillField('password', $pass); // Enter password.
$page->pressButton('Login');
$content = $this->getCurrentPageContent();
$this->assertContains('logout', $content);
}
/**
* #afterClass
*/
public function logout(){
$page = $this->getCurrentPage();
$page->clickLink('logout');
}
}
use PHPUnit\Framework\TestCase;
class MinkPetitionTest extends TestCase
{
use MinkSetup;
public function testSubmitPage(){
$this->login('user', 'pw'); // Login first.
$this->visit('full url');
$page = $this->getCurrentPage(); // Get the page.
echo $page;
$page->fillField('form_ban_id', '1234');
$page->pressButton('form_find_student');
$content = $this->getCurrentPageContent(); // Get page content.
$this->assertContains('<u>No Petitions</u> exist for Some User Student ID: 1234', $content);
}
}
$client = new MinkPetitionTest(); //tried to get something to work
$client->testSubmitPage(); //same here

You need to change your test class like so if you are using the latest phpunit:
...
use PHPUnit\Framework\TestCase;
class MinkPetitionTest extends TestCase
{
...
Can you try that first and see the result?
EDIT #2
Also, your trait file is incorrect. it should be:
trait MinkSetup
{
private $minkBaseUrl;
...
public function visit($url)
{
$this->minkSession->visit($this->minkBaseUrl . $url);
}
...
Try that

Related

How to output value of array which is set by php class?

I hope someone can help me with my problem. I'm still a beginner in PHP so sorry.
My code looks like this:
class Component
{
public $title;
// value if nothing gets set
public function __construct($title = "Test") {
$this->title = $title;
}
public function setTitle($value)
{
$this->title = $value;
}
public function getTitle()
{
return "Title: ".$this->title;
}
public function returnInfo()
{
$info = array(
'Titel' => $this->title,
);
return $info;
}
So in the class "Component" the functions should set and get a specific value. If nothing is set for a.e. title it should get the value "Test". With returnInfo() the informations like title should get returned.
My other class (where someone can add the informations like title) looks like this:
abstract class ComponentInfo extends Component
{
protected function getComponentInfo ()
{
$button1 = new Component;
$button1->setTitle("Button-Standard");
// should return all infos for button1
$button1Info = $button1->returnInfo();
foreach ($button1Info as $info)
{
echo ($info);
}
}
}
So it should work like this: in a other class named ComponentInfo a user can add a component like a button. Then the user can set informations like the title. And after that the information should get saved in an array and now I want to display all informations like this:
Title: Button-Standard
How can it work? And where is the mistake in my code?
It would be helpful to get a working code where the user can make as much ComponentInfo classes he want and where he can add different components with information that can be saved into an array.
And at the end it should get outputed as text on a main page.
You can't instantiate an abstract class. You need to remove the abstract keyword from the ComponentInfo class.
UPDATE given the info in your comment, I'd go this
Component.php
abstract class Component
{
private $key;
private $title;
public function __construct($key, $title) {
$this->setKey($key);
$this->setTitle($title);
}
public function setKey($key)
{
$this->key = $key;
}
public function getKey()
{
return $this->key;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
public function __toString()
{
return $this->getKey().': '.$this->getTitle();
}
}
ComponentInfo.php
class ComponentInfo extends Component
{
public function __construct($key='Info', $title='example title')
{
parent::__construct($key, $title);
}
}
And then use it in your code
somefile.php
$components = [];
$components []= new ComponentInfo();
$components []= new ComponentInfo('Different Key', 'Other info');
$components []= new ComponentNavigation('longitude', 'latidude'); //create another class like ComponentInfo
[... later you want to print this info in a list for example]
echo '<ul>';
foreach($components as $components) {
echo '<li>'.$component.'</li>'; //the __toString() method should get called automatically
}
echo '</ul>';
This should work, however, having different components with no other specificities than a different title and key is pointless. Instead, you could simply have different Components with a different key and title.

Every time I try to log in or press login, I am stuck at the run method

I am trying login through my login page. Each time I enter a value in the fields or just press login I notice that on my browser it stops at the run method and my page is left blank.
My database has two columns with the actual title "Username" and "Password".
I can't seem to figure out what is causing this.
Here is my login_model.php file:
<?php
class Login_Model extends BaseModel {
public function __contruct(){
parent:: __construct();
}
public function run() {
$sth = $this->database->prepare("SELECT id FROM users WHERE
Username = :username AND Password = :password");
$sth->execute(array(
':username' => $_POST['username'],
':password' => $_POST['password']
));
//$data = $sth->fetchAll();
$count = $sth->rowCount();
if ($count > 0){
//login
Session::init();
Session::set('loggedIn', true);
header('location: ../controllers/dashboard');
} else {
//show error
header('location: ../login/loginIndex');
}
}
}
?>
This is what I have within my login.php file from the controller:
<?php
class Login extends BaseController {
function __construct(){
parent::__construct();
}
//this is to avoid interference with the page that I want to call
function index() {
$this->view->render('../views/login/loginIndex');
}
function run(){
$this->model->run();
}
// public function other() {
// require '../models/loginModel.php';
// $model = new loginModel();
// }
}
This is the place I am trying to end up at:
<?php
class Dashboard extends BaseController {
function __construct(){
parent::__construct();
Session::init();
// You set the variable session: LoggedIn if they pass the condition
$logged = Session::get('loggedIn');
if ($logged == false){
Session::destroy();
header('location: ../login/loginIndex');
exit;
}
}
//this is to avoid interference with the page that I want to call
function index() {
$this->view->render('../views/dashboard/adminPage');
}
}
Update:
So after adding the following code you mentioned, this is what I got:
Notice: Undefined property: Login::$model in /apps/help-i-need-a-tutor/controllers/login.php on line 19
Fatal error: Call to a member function run() on a non-object in /apps/help-i-need-a-tutor/controllers/login.php on line 19
How do I go about finding out the cause of my issue here? I've had a look at the run method in my login_model.php class and cannot seem to find where this error may be coming from.
The error message is saying that you don't have an instance of Login_Model within Login.
Try inserting private $model above your constructor within Login, and then assigning to it within your constructor with $model = new Login_Model()

Testing your code for multiple browsers using phpunit & php-webdrivers by facebook

I am working with php-webdrivers by facebook for selenium to implement integration testing for my site. I am using phphunit to run my tests and have wrapped my code inside the phpunitframework_testcase extended class:
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$capabilities = array( WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX );
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
} catch (Exception $e){
echo $e->getMessage();
}
}
/**
* #AfterMethod
*/
public function tearDown()
{
$this->driver->close();
}
public function testImageUpload()
{
$errorSnaps='';
$myBrowserDriver = $this->driver;
//open the url
$myBrowserDriver->get($this->url);
try{
//get email field in login page
$emailField = $myBrowserDriver->findElement(WebDriverBy::id('email'));
//check if the field is displayed
if(!$emailField->isDisplayed()) {
try {
$errorSnaps = $this->takeScreenshot();
$this->errorLogs[] = "The email input Element is not present, a screen-shot of the error has been placed here --> " . $errorSnaps;
} catch (Exception $e){
$this->errorLogs[] = $e->getMessage();
}
}
} catch (NoSuchElementException $nse){
try {
$errorSnaps = $this->TakeScreenshot();
$this->errorLogs[] = "The email field on ".$this->driver->getCurrentURL()." not found , a screen-shot for the error has been placed here -->" . $errorSnaps;
}catch (Exception $e){
$this->errorLogs[]=$e->getMessage();
}
}
}
php-webdrivers documentation recommends this way to initialize the browser drivers
$capabilities=array(
\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX
);
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
but does not provide a mechanism to init multiple browser drivers to run my tests with only single test file means considering the code above I have to make different copies for all those browsers and with only one line code difference means if i want to run the test above for chrome then i have to change the line from
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX);
to
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::CHROME);
and save that code with rest all the same code as above in a different file and run my test suit. As you can see this is not an optimal way for implementing my tests and for the sake of code re-usability.
I came across 2 options:
Pass argument from the terminal sending the browser name with parameter like phpunit brName=chrome and getting it via $_SERVER['brName']. I would still have to type in each time I want to run tests for any other browser.
I came across the TestDecorator class below on phpunit site which looked like a more conventional way to achieve what I am doing but could not figure out how would I use it to run my tests.
Where should I put my code so that it detects and runs it? Every time I try to run the below sample code it says no tests were executed. If i have sample test function below how could I run it 4 times using the testdecorator as base class?
Sample Test:
public function sampleTest(){
$this->assertTrue(TRUE);
}
Test Decorator class:
require_once 'PHPUnit/Extensions/TestDecorator.php';
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;
public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);
if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}
public function count()
{
return $this->timesRepeat * $this->test->count();
}
public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}
for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}
return $result;
}
}
I was not going in the right direction to implement this scenario I should not use the testdecorator class but a more good and easy approach would be.
Setting an environment variable via cmd-line/terminal like
export BROWSER_REQUESTED=chrome for (unix) and set BROWSER_REQUESTED=chrome for (windows),or you can create a .sh file with the following code
export BROWSER_REQUESTED=chrome && phpunit and run it via cmd-line.
Create a BrowserFatory class which listens to the environment variable and initiates the drivers for the browser type requested.
create as much .sh files as much browsers you need to run the test for, i have added the code below to be more descriptive.
WebTest.php
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$this->driver = BrowserFactory::drivers();
} catch (Exception $e){
echo $e->getMessage();
}
}
}
BrowserFactory.php
class BrowserFactory
{
public static function drivers()
{
switch ($_SERVER['BROWSER_REQUESTED']) {
case 'chrome':
return self::createChrome();
break;
case "ie":
throw new Exception('Not implemented');
break;
case 'firefox':
default:
return self::createFirefox();
break;
}
}
public static function createChrome()
{
putenv("webdriver.chrome.driver=/path/to/chromedriver");
$service = ChromeDriverService::createDefaultService();
$service->start();
return ChromeDriver::start(DesiredCapabilities::chrome(), $service);
}
public static function createFirefox()
{
// these are just constants defined in bootstrap.php
$seleniumUrl = isset($_SERVER['JENKINS_URL']) ? TEST_ENV_SELENIUM_SERVER : LOCAL_ENV_SELENIUM_SERVER;
return RemoteWebDriver::create(
$seleniumUrl, DesiredCapabilities::firefox()
);
}
}
command-line.sh
export BROWSER_REQUESTED='chrome' && phpunit

PHP extends namespace\class unknown error

This is my parent class code: \core\controller\controlmaster
<?php
namespace core\controller;
class controlmaster{
private $model_base = null;
//public function __construct(){
//always start session for any loaded controller
//session_start();
//}
public function _loadModels($models){
$file = dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR.$this->model_base.$models.".php";
$file_alloc = str_replace("\\","/",$file);
if(file_exists($file_alloc)){
require($file_alloc);
$models_name = $this->model_base.$models;
return new $models_name();
}
}
public static function _setModelBase($model_location){
$this->model_base = $model_location;
}
}
?>
and this is my controller page for application : \applications\controller\index
<?php
namespace applications\controllers;
use core\configuration\configloader as config;
class index extends \core\controller\controlmaster{
public $config;
public function __construct(){
$this->config = new config;
$this->config->_parsePHP("j3mp_setting.php");
parent::_setModelBase($this->config->settings['app_model_base']); // Error doesn't appear when i comment this function
echo "This is main page and this is config for model app base : {$this->config->settings['base_url']}";
}
}
?>
This is core\configuration\configloader:
<?php
namespace core\configuration;
class configloader{
//Contains setting informations
public $inisettings;
public $settings;
public function _parseINI($file,$section){
$section = empty($section) ? false : true;
$file = __DIR__.DIRECTORY_SEPARATOR.$file;
if(file_exists($file)){
$parse = parse_ini_file($file,$section);
$this->inisettings = $parse;
}else{
throw new core\errorhandler\exception("File {$file} is not found in our system. Please contact administrator for details.","configloader");
}
}
public function _parsePHP($file){
$file = __DIR__.DIRECTORY_SEPARATOR.$file;
if(file_exists($file)){
$settings = array();
include($file);
$this->settings = $settings;
}else{
throw new core\errorhandler\exception("File {$file} is not found in our system. Please contact administrator for details.","configloader");
}
}
}
?>
When i comment "parent::_setModelBase(...)" code, the error disappear and the browser successfully print "This is main page and this is config for model app base : http://iosv3.net/". I think the error come from \core\controller\controlmaster, but I don't know how to fix that? I always try to edit the file (\core\controller\controlmaster) but notting happens... If error occured, the message ("This is main page ...") doesn't come out... Could you show me where is the error come from? Thanks...

method save - get current ID

class News extends BaseNews
{
public function save(Doctrine_Connection $conn = null)
{
$this->setUserId($this->getAttribute('user_id'));
return parent::save($conn);
}
}
How can i get here current save News ID? I would like add this for session, but i dont know how can i get this?
Never used symfony but as far as I understand you, you want something like this:
class News extends BaseNews
{
public function save(Doctrine_Connection $conn = null)
{
$this->setUserId($this->getAttribute('user_id'));
$result = parent::save($conn);
echo $this->getId(); // Your news Id
return $result;
}
}

Categories