I have about 500 possible paths to a particular page, and I need to test all of them. Each path to the that page looks similar to this (using PHP web driver; usually has about 10 steps):
// Navigate to form
$driver->get('http://www.domain.com');
$driver->get($driver->findElement(WebDriverBy::xpath("//a[contains(text(),'Foo 1')]"))->getAttribute('href'));
$driver->findElement(WebDriverBy::xpath("//div[#class='countryHeader']//a[contains(text(), 'Bar 1')]"))->click();
$driver->findElement(WebDriverBy::xpath("//form[#name='formDisclaimer']//input[contains(#class, 'button')]"))->click();
I don't want to have to write code for all the steps for all possible paths to the page. I do, however, have all the pertinent details of the steps (e.g. the XPath, the string the node may contain, etc.) in a database.
Is there a way for me to "dynamically" produce some sort of configuration file (either in XML or JSON) that I can feed to the driver as a set of instructions for it to follow?
A long time back at one of my project I had a similar requirement. I tried to create a Robot (or someone may call Web Crawler). As I started navigating through the pages I started maintaining the navigation paths in spreadsheet, so I don't have to click on the paths manually. Once I have the paths, next time whenever a Path changes I will be notified and if it is a valid change then make that change in s/s or raise it as a bug.
As you said you have all relevant details in the database then you just can simply read it and in a foreach loop pass to selenium driver.
or if you don't want to have a reference to the database in your test, just dump data to PHP array and add to your test class.
You just need to write a logic to transform your sql data into test. Don't need to write every test manually.
I don't know which testing framework you are using, but you can execute many tests from a single test for example in PHPUnit that would be something like:
class My_PathsTest extends PHPUnit_Framework_TestCase
{
public function setUp() {
// setup $this->tests here
}
public function testAll() {
// $this->tests would contain info about paths taken from database.
$failures = array();
foreach($this->tests as $paths_set) {
try {
/**
* $driver->get($paths_set['start_point']);
* foreach ($paths_set['paths'] as $path ) {
* $driver->findElement(WebDriverBy::xpath($path));
* }
*
* Important!!!
* If you didn't find something you expected
* just throw the PHPUnit_Framework_ExpectationFailedException exception
* throw new PHPUnit_Framework_ExpectationFailedException('Element missing add some info here about which is missing etc..');
*/
}
catch(PHPUnit_Framework_ExpectationFailedException $e) {
$failures[] = $e->getMessage();
}
}
if (!empty($failures)) {
throw new PHPUnit_Framework_ExpectationFailedException(count($failures) . " assertions failed:\n\t" . implode("\n\t", $failures));
}
}
}
best is to get data from db with odbc as a list (array) xpath locators and then loop over it.
If you don't have a direct access to the db, export the query results as a .csv file (MS db has an option save as, not sure about the others) and then read the file and loop over the array
Related
There are two noisy console commands in my Laravel 5.3 app that I want to keep logs for but would prefer to have them write to a different log file from the rest of the system.
Currently my app writes logs to a file configured in bootstrap/app.php using $app->configureMonologUsing(function($monolog) { ...
Second prize is writing all console commands to another log file, but ideally just these two.
I tried following these instructions (https://blog.muya.co.ke/configure-custom-logging-in-laravel-5/ and https://laracasts.com/discuss/channels/general-discussion/advance-logging-with-laravel-and-monolog) to reroute all console logs to another file but it did not work and just caused weird issues in the rest of the code.
If this is still the preferred method in 5.3 then I will keep trying, but was wondering if there was newer method or a method to only change the file for those two console commands.
They are two approaches you could take
First, you could use Log::useFiles or Log::useDailyFiles like suggests here.
Log::useDailyFiles(storage_path().'/logs/name-of-log.log');
Log::info([info to log]);
The downside of this approach is that everything will still be log in your default log file because the default Monolog is executed before your code.
Second, to avoid to have everything in your default log, you could overwrite the default logging class. An exemple of this is given here. You could have a specific log file for let's say Log::info() and all the others logs could be written in your default file. The obvious downside of this approach is that it requires more work and code maintenance.
This is possible but first you need to remove existing handlers.
Monolog already has had some logging handlers set, so you need to get rid of those with $monolog->popHandler();. Then using Wistar's suggestion a simple way of adding a new log is with $log->useFiles('/var/log/nginx/ds.console.log', $level='info');.
public function fire (Writer $log)
{
$monolog = $log->getMonolog();
$monolog->popHandler();
$log->useFiles('/var/log/nginx/ds.console.log', $level='info');
$log->useFiles('/var/log/nginx/ds.console.log', $level='error');
...
For multiple handlers
If you have more than one log handler set (if for example you are using Sentry) you may need to pop more than one before the handlers are clear. If you want to keep a handler, you need to loop through all of them and then readd the ones you wanted to keep.
$monolog->popHandler() will throw an exception if you try to pop a non-existant handler so you have to jump through hoops to get it working.
public function fire (Writer $log)
{
$monolog = $log->getMonolog();
$handlers = $monolog->getHandlers();
$numberOfHandlers = count($handlers);
$saveHandlers = [];
for ($idx=0; $idx<$numberOfHandlers; $idx++)
{
$handler = $monolog->popHandler();
if (get_class($handler) !== 'Monolog\Handler\StreamHandler')
{
$saveHandlers[] = $handler;
}
}
foreach ($saveHandlers as $handler)
{
$monolog->pushHandler($handler);
}
$log->useFiles('/var/log/nginx/ds.console.log', $level='info');
$log->useFiles('/var/log/nginx/ds.console.log', $level='error');
...
For more control over the log file, instead of $log->useFiles() you can use something like this:
$logStreamHandler = new \Monolog\Handler\StreamHandler('/var/log/nginx/ds.console.log');
$pid = getmypid();
$logFormat = "%datetime% $pid [%level_name%]: %message%\n";
$formatter = new \Monolog\Formatter\LineFormatter($logFormat, null, true);
$logStreamHandler->setFormatter($formatter);
$monolog->pushHandler($logStreamHandler);
********* Updated question **************
So I have tried to implement my own AttrDef to HTMLPurifier but it doesn't "take", and I can't debug using die() either.
Here's what I have:
I created Transform.php in the HTMLPurifier/AttrDef/CSS/ directory. The only contents so far is this (I'm only trying to hook it in for now, I will add validating logics once I see that it is in the loop and thus can test it):
<?php
/**
* Validates Transform as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Transform extends HTMLPurifier_AttrDef
{
//basing this off of the color definition so the var is $color for now, may change it to $transform later
public function validate($color, $config, $context) {
return $color;
}
}
I added my file to library/HTMLPurifier.includes.php like this:
require 'HTMLPurifier/AttrDef/CSS/Transform.php';
and to the library/HTMLPurifier.safe-includes.php
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Transform.php';
(not sure about the difference between these two include files above but all AttrDef files seemed to be in both so I added my file to both as well).
Then I try to make use of this new definition by adding this to library/HTMLPurifier/CSSDefinition.php:
// transform
$this->info['transform'] = new HTMLPurifier_AttrDef_CSS_Transform();
It is as if all of my additions were never made, and I can't debug it by putting a die() in my own file either, nothing happens.
So any advice on where I went wrong or how I can debug this is very much appreciated.
*********** addition *******
I also tried a simple bypass by applying the Color-AttrDef to any transform property, in the CSSDefinition.php:
$this->info['transform'] = new HTMLPurifier_AttrDef_CSS_Color();
And I hacked the original Color definition like this:
//TODO: testing ways to bypass
if (strpos($color, 'rotate(') !== false) {
return $color;
}
Not working. Please advice on what I am missing.
You'll need to define your own AttrDef which knows how to parse and validate such definitions. Color should serve as a decent model, since the rgb syntax is similar to matrix.
On a project i am working we use Symfony2 console commands to run image converting (using LaTeX and some imagick). Due to the nature of project, not all conditions may be met during the console command run so the execution will fail, to be later restarted with a cron job, only if attempts count is not higher that predefined limit.
We already hove logging in our project, we use Monolog logger. What i basically want is to somehow duplicate everything that goes to the main log file in another log file, created specifically for that console command execution and only if attempts limit is reached.
So, if we run command once and it fails - it's ok and nothing should be created.
But if we run command for the 10th time, which is attempt limit, i want to have a separate log file named, say '/logs/failed_commands//fail.log'. That log file should only have messages for the last failed attempt, but not for all the previous ones.
How to do that? Do i need some combination of special logger handler (like FingersCrossed) and proper exceptions handling? Should i rather create additional instance of logger (if so, how can i pass it over to dependent services?)
This is simplified and cleaned piece of command that runs images converting. The attempts limit is checked withing the $this->checkProcessLimit() method
public function execute(InputInterface $input, OutputInterface $output)
{
try {
set_time_limit(0); //loose any time restrictions
$this->checkingDirectories();
$this->checkProcessLimit();
$this->isBuildRunning();
$this->checkingFiles();
try {
$this->startPdfBuilding();
} catch (InternalProjectException $e) {
throw PdfBuildingException::failedStartBuilding($this->pressSheet->getId());
}
} catch (PdfBuildingException $e) {
$this->printError($output, $e);
return;
}
try {
$this->logger->info('Building Image.');
$this->instantiatePdfBuilder();
$buildingResult = $this->pdfBuilder->outputPdf();
$this->generatePreview($buildingResult);
$this->movePDFs($buildingResult);
$this->unlinkLockFile();
$output->writeln('<info>Image successfully built</info>');
} catch (LaTeXException $e) {
$this->unlinkLockFile();
$this->abortPdfBuilding($e->getMessage());
$this->printError($output, $e);
return;
}
}
UPD: It seems that for dumping a bunch of log entries i need to use BufferHandler bundled with Monolog Logger. But i still need to figure out the way to set it up to get dumps only when errors limit (not error level) reached.
UPD2: I've managed to make it work, but i don't like the solution.
Since in Symfony2 you have to define loggers in config.yml and have to rebuild cache for any changes in configuration, i had to resort to dynamically adding a handler to a logger. But the logger itself is considered to be of Psr\Log\LoggerInterface interface, which does not have any means to add handlers. The solution i had to use actually checks if used logger is an instance of Monolog\Logger and then manually adding a BufferHandler to it in Symfony2 Console command's initialize() method.
Then, when it comes to the point where I check for attempts limit, i close buffer handler and delete actual log file (since BufferHandler has no means to removing/closing itself without flushing all it's contents) if limit is not yet reached. If it is, i just let the log file to stay.
This way it works, but it always writes the log, and i have to remove logs if condition (reached attempt limit) is not met.
i think you must create a custom handler.
With Monolog, you can log in a database (see for example https://github.com/Seldaek/monolog/blob/master/doc/04-extending.md)
Thus, it's easy to know how many times an error was raised since x days.
(something like : "select count(*) from monolog where channel='...' and time>...")
I apologise if this has already been answered somewhere, but I haven't managed to find an answer so far - maybe I'm searching for the wrong thing!
I am trying to figure out how to handle errors in my OO PHP system, which is used to generate web pages. Hopefully this example will explain what I mean.
Imagine I have a Content class, a Form class and a FormObject class, which hold all the information on page content, web forms and form fields. All classes can run multiple MySQL queries via the DB class.
Users can create new content or forms in the back-end. When they do this, I use the classes to create and store the data in the database.
I also have a System class, which is used to generate the web pages. The System class checks what should be displayed on the front-end, builds the appropriate Content and Form objects, then generates the HTML and outputs it to the screen.
I have some checks for serious errors, which stop the code from going any further. However, the problem is that I want to feed back some "soft errors" to the front-end. For example, maybe the System class builds a Form object, which in-turn builds the fields using the FormObject class. The FormObject class queries the database for a field name, but a field name is not found. So the DB class returns an error. I want to be able to feed back a message to the front-end that says the field name has not been found.
What is the best way to get that "soft error" message back to the System class, so it can be outputted to the front-end?
I realise it is fairly simple in this particular example, but as more classes are added and, crucially, more levels are added, the problem becomes a bit bigger.
One way I thought of doing this was to have an Error class. The system would create an Error object and pass it on to each Content and Form object as they are created. The Form class would pass the same Error object to the FormItem class. Whenever an error is found, it is logged via a method in the Error class. The system can then access the original Error object and output all the errors. However, as the system grows, more classes are added, and more objects are created, it could get quite confusing. Is there a better way?
You might want to use either
something global that all classes can access (e.g. a global variable or a Singleton), or
something that is passed in to all instantiations of classses producing what you call 'soft errors'
to collect such errors. You then want to use whatever you collected and add it to the output in your System class somehow.
To be more specific...
This is an example for the solution using a global:
global $softErrorMessages = array();
class A
{
function sampleFunctionA()
{
// [...]
// some code setting $result to some valid value
// or to false if an error occured
if($result === false) // check for validity
{
global $softErrorMessages;
$softErrorMessages[] = "The sample function A caused a soft error";
return;
}
// [...]
// some code requiring a valid $result
}
}
If you use such a global, you can then easily access it from your System class and put its contents into the right places of your output.
However, if you perform unit tests, you might not want to use globals or global-like solutions (like singletons). So here is an example for an 'error collection' approach:
class ErrorCollector
{
private $errors = array();
function addError($error)
{
$this->errors[] = $error;
}
function getErrors()
{
return $this->errors;
}
}
class A
{
private $errorCollector;
function __construct(/* your additional parameters */, ErrorCollector $errorCollector)
{
// [...]
// additional instantiation stuff
$this->errorCollector = $errorCollector;
}
function sampleFunctionA()
{
// [...]
// some code setting $result to some valid value
// or to false if an error occured
if($result === false) // check for validity
{
$this->errorCollector->addError("The sample function A caused a soft error");
return;
}
// [...]
// some code requiring a valid $result
}
}
You would instantiate the ErrorCollector only once and then pass it to all other class instantiations. Then you let your objects perform their duties (and possibly add soft errors to the ErrorCollector). Once they're done, your System class would then get all the error messages and - again - place them at the right place of your output.
Exceptions is a convenient mechanism to handle errors. FormObject can throw an exception of some SoftErrorException class if DB returns an error. And then in System you are catching this exception and render it to front-end.
class System {
public function showFormAction() {
try {
$form = ... // create a form
$this->renderForm($form);
} catch (SoftErrorException $e) {
$this->handleSoftError($e);
}
}
public function handleSoftError(SoftErrorException $e)
{
// Do whatever you want with exceptions: render it
// $this->renderErrorPage($e->getMessage());
// or collect them and show after
// $this->errors[] = $e;
}
}
How should I write error reporting modules in PHP?
Say, I want to write a function in PHP: 'bool isDuplicateEmail($email)'.
In that function, I want to check if the $email is already present in the database.
It will return 'true', if exists. Else 'false'.
Now, the query execution can also fail, In that time I want to report 'Internal Error' to the user.
The function should not die with typical mysql error: die(mysql_error(). My web app has two interfaces: browser and email(You can perform certain actions by sending an email).
In both cases it should report error in good aesthetic.
Do I really have to use exception handling for this?
Can anyone point me to some good PHP project where I can learn how to design robust PHP web-app?
In my PHP projects, I have tried several different tacts. I've come to the following solution which seems to work well for me:
First, any major PHP application I write has some sort of central singleton that manages application-level data and behaviors. The "Application" object. I mention that here because I use this object to collect generated feedback from every other module. The rendering module can query the application object for the feedback it deems should be displayed to the user.
On a lower-level, every class is derived from some base class that contains error management methods. For example an "AddError(code,string,global)" and "GetErrors()" and "ClearErrors". The "AddError" method does two things: stores a local copy of that error in an instance-specific array for that object and (optionally) notifies the application object of this error ("global" is a boolean) which then stores that error for future use in rendering.
So now here's how it works in practice:
Note that 'Object' defines the following methods: AddError ClearErrors GetErrorCodes GetErrorsAsStrings GetErrorCount and maybe HasError for convenience
// $GLOBALS['app'] = new Application();
class MyObject extends Object
{
/**
* #return bool Returns false if failed
*/
public function DoThing()
{
$this->ClearErrors();
if ([something succeeded])
{
return true;
}
else
{
$this->AddError(ERR_OP_FAILED,"Thing could not be done");
return false;
}
}
}
$ob = new MyObject();
if ($ob->DoThing())
{
echo 'Success.';
}
else
{
// Right now, i may not really care *why* it didn't work (the user
// may want to know about the problem, though (see below).
$ob->TrySomethingElse();
}
// ...LATER ON IN THE RENDERING MODULE
echo implode('<br/>',$GLOBALS['app']->GetErrorsAsStrings());
The reason I like this is because:
I hate exceptions because I personally believe they make code more convoluted that it needs to be
Sometimes you just need to know that a function succeeded or failed and not exactly what went wrong
A lot of times you don't need a specific error code but you need a specific error string and you don't want to create an error code for every single possible error condition. Sometimes you really just want to use an "opfailed" code but go into some detail for the user's sake in the string itself. This allows for that flexibility
Having two error collection locations (the local level for use by the calling algorithm and global level for use by rendering modules for telling the user about them) has really worked for me to give each functional area exactly what it needs to get things done.
Using MVC, i always use some sort of default error/exception handler, where actions with exceptions (and no own error-/exceptionhandling) will be caught.
There you could decide to answer via email or browser-response, and it will always have the same look :)
I'd use a framework like Zend Framework that has a thorough exception handling mechanism built all through it.
Look into exception handling and error handling in the php manual. Also read the comments at the bottom, very useful.
There's aslo a method explained in those page how to convert PHP errors into exceptions, so you only deal with exceptions (for the most part).