Is it possible to assert text in a repeated div? - behat

I am using behat/mink to create some BDD tests. I would like to know if it's possible to get a text inside a div that is repeated in the page. For example:
<div class="message">Text 1</div>
<div class="message">Text 2</div>
<div class="message">Text 3</div>
The class is repeated but the text is different. I would like to assert the text that is displayed in the second div.

You can clean/modify iReadContentOfDiv() method as you wish.
Gherkin
Scenario: Iterate classes
Given I am on "about"
Then I should see "Welcome to About page"
And The content of repeated ".message" div should be:
| content |
| Text 1 |
| Text 2 |
| Text 3 |
FeatureContext.php
namespace MyProject\ApiBundle\Features\Context;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends MinkContext
{
/**
* #When /^The content of repeated "([^"]*)" div should be:$/
*/
public function iReadContentOfDiv($class, TableNode $table)
{
$session = $this->getSession();
$page = $session->getPage();
$element = $page->findAll('css', $class);
if (null === $element) {
throw new \InvalidArgumentException(sprintf('Could not evaluate CSS: "%s"', $class));
}
$found = [];
foreach ($element as $e) {
$found[] = $e->getText();
}
foreach ($table->getHash() as $element) {
if (!in_array($element['content'], $found)) {
throw new Exception(sprintf('Data "%s" not found in DOM element "%s".', $element['content'], $class));
}
}
}
}
ABOUT page content:
<div class="message">Text 1</div>
<div class="message">Text 2</div>
<div class="message">Text 3</div>

Based on #BentCoder answer, I did a small changes to solve the problem:
/**
* #Then /^The content of repeated "([^"]*)" div should contain "([^"]*)"$/
*/
public function iReadContentOfDiv($class, $text)
{
$session = $this->getSession();
$page = $session->getPage();
$element = $page->findAll('css', $class);
if (null === $element) {
throw new \InvalidArgumentException(sprintf('Could not evaluate CSS: "%s"', $class));
}
foreach ($element as $e) {
if (strpos($e->getText(), $text)){
print 'opa';
return;
}
}
throw new Exception(sprintf('Data "%s" not found in DOM element "%s".', $text, $class));
}

http://casperjs.readthedocs.org/en/latest/modules/tester.html
This is a javascript testing API that allows you to assert anything in the dom

Related

How to show page before executing OnGet?

I have a razor page that shows a 'Please wait' box and the OnGet method does some stuff that might take a few seconds and ends with a LocalRedirect.
The razor code:
#page
#inject IStringLocalizer<Startup> localizer
<div class="login-page">
<div class="login-box">
<div class="card">
<div class="card-body login-card-body">
<div class="help-block text-center">
<div class="spinner-border" role="status" />
</div>
<p class="login-box-msg">#localizer["PleaseWait"]</p>
</div>
</div>
</div>
</div>
And the code-behind:
public async Task<IActionResult> OnGet()
{
//Do some stuff that takes a few seconds...
return LocalRedirect("/Dashboard");
}
Everything is working apart from the page first being shown and then executing the code.
Is it possible to first render the page (so that the users can see that something is happening) and then execute the code in the OnGet?
Move your long async routines from OnGet to a number of named handler methods. Allow the page to render a "please wait" message and use client-side code (jQuery AJAX or plain Fetch) to call the named handlers. Keep track of when they complete and when all have completed, redirect to the other page.
Here's an example where the OnGet simply renders the page (can include a "please wait" message"), and a number of named handlers simulate routines of varying length:
public void OnGet()
{
}
public async Task OnGetTwoSeconds()
{
await Task.Delay(2000);
}
public async Task OnGetThreeSeconds()
{
await Task.Delay(3000);
}
public async Task OnGetFiveSeconds()
{
await Task.Delay(5000);
}
The following script goes in the Razor page itself. It consists of three variables for tracking task completion and a method that redirects to the home page when all three have completed. Each of the named handlers is called by the code and sets its tracking variable to true on completion as well as calling the redirect function:
#section scripts{
<script>
let twosecondsdon = false;
let threesecondsdone = false;
let fivesecondsdone = false;
function redirect(){
if (twosecondsdone && threesecondsdone && fivesecondsdone) {
location.href = '/';
}
}
fetch('?handler=TwoSeconds').then(() => {
twosecondsdone = true;
redirect();
})
fetch('?handler=ThreeSeconds').then(() => {
threesecondsdone = true;
redirect();
})
fetch('?handler=FiveSeconds').then(()=>{
fivesecondsdone = true;
redirect();
})
</script>
}
When all three complete, the redirect function does its thing.
Is it possible to first render the page (so that the users can see
that something is happening) and then execute the code in the OnGet?
Yes you can do that. Currently, I am showing you the delay counter where you can replace your page. Please follow the steps below:
HTML:
Script:
#section scripts {
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<script>
$(document).ready(function () {
var counter = 5;
(function countDown() {
if (counter-- > 0) {
$('#timer').text("Please wait... we are redirecting you to register page..." + counter + ' s');
setTimeout(countDown, 1000);
} else {
window.location.href = "https://localhost:44361/userlog/ViewCalculateAge";// Here put your controller URL where you would like to redirect
}
})();
});
</script>
}
Output:

Exception: Service 'voltService' wasn't found in the dependency injection container

I am trying to replicate the examples in the Volt tutorial here, using the basic example of phalcon here, so nothing complicated.
So I created this app/controllers/PostsControllers like this:
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function indexAction()
{
/* $post = Post::findFirst();
$menu = Menu::findFirst();*/
$post = array("title"=>"The titre");
$menu = "menu1";
$this->view->show_navigation = true;
$this->view->menu = $menu;
$this->view->title = $post["title"];
$this->view->post = $post;
// Or...
$this->view->setVar('show_navigation', true);
$this->view->setVar('menu', $menu);
$this->view->setVar('title', $post["title"]);
$this->view->setVar('post', $post);
}
}
And its corresponding app/views/posts/index.phtml like this:
{# app/views/posts/show.phtml #}
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} - An example blog</title>
</head>
<body>
{% if show_navigation %}
<ul id='navigation'>
{% for item in menu %}
<li>
<a href='{{ item.href }}'>
{{ item.caption }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
<h1>{{ post.title }}</h1>
<div class='content'>
{{ post.content }}
</div>
</body>
</html>
I also registered volt in my bootstrap file (/public/index.php) which looks like that:
<?php
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Application;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Url as UrlProvider;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;
use Phalcon\Mvc\View\Engine\Volt;
// Register an autoloader
$loader = new Loader();
$loader->registerDirs(
[
"../app/controllers/",
"../app/models/",
]
);
$loader->register();
// Create a DI
$di = new FactoryDefault();
// Setup the view component
$di->set(
"view",
function () {
$view = new View();
$view->setViewsDir("../app/views/");
$view->registerEngines(
[
'.volt' => 'voltService',
]
);
return $view;
}
);
// Setup a base URI so that all generated URIs include the "tutorial" folder
$di->set(
"url",
function () {
$url = new UrlProvider();
$url->setBaseUri("/care/");
return $url;
}
);
$application = new Application($di);
try {
// Handle the request
$response = $application->handle();
$response->send();
} catch (\Exception $e) {
echo "Exception: ", $e->getMessage();
}
But when I try to access to /posts directory (localhost/care/posts) I get the following error:
Exception: Service 'voltService' wasn't found in the dependency injection container
I checked if Volt service was not already declared in Services.php as it is said in a similar post here but it is not.
Thank you
The issue is with this block of code. You are telling your view that it should use the volt extension and process it using a service called voltService.
// Setup the view component
$di->set(
"view",
function () {
$view = new View();
$view->setViewsDir("../app/views/");
$view->registerEngines(
[
'.volt' => 'voltService',
]
);
return $view;
}
);
If you look at your snippet, there is no service called voltService defined.
However if you add this to your services, it should work:
// Register Volt as a service
$di->set(
'voltService',
function ($view, $di) {
$volt = new Volt($view, $di);
$volt->setOptions(
[
'compiledPath' => '../app/compiled-templates/',
'compiledExtension' => '.compiled',
]
);
return $volt;
}
);
Reference: https://docs.phalconphp.com/en/3.2/volt#setup

Prestashop: display "Choose language" inline in admin

I wanna simplify my life and display language flags inline next to input fields in admin panel.
Example:
Turn this:
into this:
I tried override
abstract class ModuleCore { public function displayFlags() }
but no effect.
Then I modify admin\themes\default\template\helpers\options\options.tpl to:
<div class="displayed_flag">
{foreach $languages as $language}
<img src="../img/l/{$language.id_lang}.jpg"
class="pointer"
alt="{$language.name}"
title="{$language.name}"
onclick="changeLanguage('{$key}', '{if isset($custom_key)}{$custom_key}{else}{$key}{/if}', {$language.id_lang}, '{$language.iso_code}');" />
{/foreach}
</div>
But still nothing.
Of course I deleted class_index.php, clear cache etc...
I am using Prestashop 1.5.5 and default theme.
You're searching for the displayFlags function inside /js/admin.js file.
Here it works on my installation with this changes:
function displayFlags(languages, defaultLanguageID, employee_cookie)
{
if ($('.translatable'))
{
$('.translatable').each(function() {
if (!$(this).find('.displayed_flag').length > 0) {
$.each(languages, function(key, language) {
if (language['id_lang'] == defaultLanguageID)
{
defaultLanguage = language;
return false;
}
});
var displayFlags = $('<div></div>')
.addClass('displayed_flag');
$.each(languages, function(key, language) {
var img = $('<img>')
.addClass('pointer')
.css('margin', '0 2px')
.attr('src', '../img/l/' + language['id_lang'] + '.jpg')
.attr('alt', language['name'])
.click(function() {
changeFormLanguage(language['id_lang'], language['iso_code'], employee_cookie);
});
displayFlags.append(img);
});
if ($(this).find('p:last-child').hasClass('clear'))
$(this).find('p:last-child').before(displayFlags);
else
$(this).append(displayFlags);
}
});
}
}

getElement by id and loop through all elements inside in nightwatch

<div id="productcontainer" class="products-list" style="display: block;">
<a id="prd_Item_0" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a></div>
var ele=document.getElementById("productcontainer");
if(ele)
{
tags=ele.getElementsByTagName("a");
for(i=0;i<tags.length;i++)
{
if(tags[i])
{
tags[i].click();
}
}
}
I want to do like above in nightwatch js, can anyone help me how to do it like above using nightwtch js.
Thanks in advance
Use the promise returned by element finder findElements and then loop through the anchor elements to perform any operations that you want. Try the below code -
driver.findElements(By.cssSelector('#productcontainer a')).then(function(tags){
for (var i = 0; i< tags.length; i++){
if(tags[i])
tags[i].click();
}
});
Hope this helps.
This should do the job ..
module.exports = {
'Iterate over elements and click them': function(browser) {
function iterate(elements) {
elements.value.forEach(function(el) {
browser.click(el.ELEMENT, function(r) {
browser.assert.ok(r.status === 0);
});
});
}
browser
.url('..')
.elements('css selector', 'div#productcontainer a', iterate)
.end();
}
};

AJAX Cascading with MVC4

I used the below method for doing Async postback using AJAX. This works fine on clicking submit. But i would like to know, is that possible to call various ActionMethods in a controller via AJAX.
I would like to implement something like cascading dropdown. How to call different ActionMethod via AJAX on dropdown value change?
Here is the code which call only one ActionMethod on submitting form.
View
#{
ViewBag.Title = "Index";
var options = new AjaxOptions()
{
Url = Url.Action("Index", "City"),
LoadingElementId = "saving",
LoadingElementDuration = 2000,
Confirm = "Are you sure you want to submit?"
};
}
<h2>Index</h2>
#using (Ajax.BeginForm(options))
{
<div id="saving">Loading...</div>
#Html.DropDownList("Countries",ViewBag.Countries as SelectList)<input type="submit" />
}
Controller
public ActionResult Index()
{
IEnumerable<SelectListItem> selectListItems = new []
{
new SelectListItem{ Text = "US",Value = "1" }
};
ViewBag.Countries = selectListItems;
return View();
}
public ActionResult GetState(string countryId)
{
IEnumerable<SelectListItem> selectListItems = new[]
{
new SelectListItem { Text = "Tennesse", Value = "1" },
new SelectListItem { Text = "Newyork", Value = "2" }
};
return View();
}
The answer to your first question "is that possible to call various ActionMethods in a controller via AJAX" is a big yes. You may call any action method from your controller through Ajax though the only result generated depends on various things like whether you send a view or partial view or JSON result.
for your next question :
I will be posting some codes
Controller.cs
public JsonResult getCity(string country)
{
var temp = (from cntry in db.Table3.OrderBy(s => s.country)
where (string.Compare(cntry.country, country) == 0)
select cntry.city).ToList();
return Json(temp, JsonRequestBehavior.AllowGet);
}
View
<h1>
Countries</h1>
<select name="countries" class="combo">
<option value=""></option>
#{
foreach (var t in (List<string>)ViewBag.countries)
{
<option value=#t>#t</option>
}
}
</select>
<h1>
State</h1>
<select name="city" class="combo2">
</select>
<div id="tese">
</div>
#*
The following jquery code finds the selected option from country dropdown
and then sends an ajax call to the Home/getcity method
and finally populate it to the city dropdown
*#
<script type="text/javascript">
$('body').on('change', '.combo', function () {
var selectedValue = $(this).val();
alert(selectedValue);
$.get("/Home/getcity", { country: selectedValue }, function (data) {
$("#tese").html(data);
$(".combo2").html("<option value = \"\"></option>")
$.each(data, function (index, value) {
$(".combo2").append("<option value = \"" + value + "\">" + value + "</option>");
});
$(".combo2").html()
});
});
</script>
This will show a dropdown of countries list. Once a country is selected it will render a new dropdown of city list
public JsonResult getCity(string country)
{
var temp = (from cntry in db.Table3.OrderBy(s => s.country)
where (string.Compare(cntry.country, country) == 0)
select cntry.city).ToList();
return Json(temp, JsonRequestBehavior.AllowGet);
}
View
<h1>
Countries</h1>
<select name="countries" class="combo">
<option value=""></option>
#{
foreach (var t in (List<string>)ViewBag.countries)
{
<option value=#t>#t</option>
}
}
</select>
<h1>
State</h1>
<select name="city" class="combo2">
</select>
<div id="tese">
</div>
#*
The following jquery code finds the selected option from country dropdown
and then sends an ajax call to the Home/getcity method
and finally populate it to the city dropdown
*#
<script type="text/javascript">
$('body').on('change', '.combo', function () {
var selectedValue = $(this).val();
alert(selectedValue);
$.get("/Home/getcity", { country: selectedValue }, function (data) {
$("#tese").html(data);
$(".combo2").html("<option value = \"\"></option>")
$.each(data, function (index, value) {
$(".combo2").append("<option value = \"" + value + "\">" + value + "</option>");
});
$(".combo2").html()
});
});
</script>