In my tests I fill the input fields (of Ant.Design https://ant.design/components/input/) with app.client.setValue( selector, value ) method. According to https://github.com/electron-userland/spectron it is supposed to be Selenium Webdriver's method. Ans it says here https://webdriver.io/docs/api/element/setValue.html the mehtod overrides the value in input. To me it always acts like addValue (meaning it sends the keys on top of the current value in the input) and I cannot find a workaround.
Resetting the input on the client just before setValue by some reason doesn't work...
async type( selector, value ) {
await this.app.client.execute( ( selector ) => {
const el = document.querySelector( selector );
// When it's INPUT_NUMBER reset is 0
el.setAttribute( "value", el.classList.contains( "ant-input-number-input" ) ? 0 : "" );
}, selector );
return await this.app.client.setValue( selector, value );
}
Related
I have a simple client script to validate select field, even if I put only false to return its always validate ( you can pass to next field, there is no focus)
Is it a known problem with that select fields?
function validateField(scriptContext) {
var field = scriptContext.fieldId;
if (field === 'custbody_select') {
return false
}
return false
}
Imagine waiting for multiple selectors like this:
const element = await page.waitForSelector("div#test div#test2 div#test3 button, div#zest div#zest2 div#zest3 button")
How do I tell whether the first selector matched, or the second one? Do I need to $ twice and then compare the element?
You are using , separator which is the "multiple" operator.
To see which element you have, check the id of the parent.
const element = await page.waitForSelector("div#test div#test2 div#test3 button, div#zest div#zest2 div#zest3 button")
const parent = await element.$('xpath=..')
if (parent) {
const id = await parent.getAttribute('id')
console.log('id', id)
expect(id).to.eq('test3') // passes in test3 is present
// or if test3 is not present
expect(id).to.eq('zest3') // zest3 passes if test3 is not present
}
You can separate the selectors using a comma, which acts as an OR condition, and then extract the innerText of those elements. And then on the basis of the inner text, determine which selector was available.
const elementText = await page
.locator(
'div#test div#test2 div#test3 button,div#zest div#zest2 div#zest3 button'
)
.innerText()
if (elementText == 'some text') {
//Do something
}
In case you have an attribute-value pair that is unique to both, you can do like this:
const elementText = await page
.locator(
'div#test div#test2 div#test3 button,div#zest div#zest2 div#zest3 button'
)
.getAttribute('id')
if (elementText == 'zest') {
//Do something
}
I can't find any code example or docs that answers this:
Achieve almost complete infinite scroll -> unknown # of items, but there is a finite amount that may be infeasible to compute beforehand - e.g. at some point the list needs to stop scrolling
Can I trigger first load of data from within InfiniteScroller/List - it seems you need to pass in a data source that is populated with initial page
I am using this example:
https://github.com/bvaughn/react-virtualized/blob/master/docs/creatingAnInfiniteLoadingList.md
and:
https://github.com/bvaughn/react-virtualized/blob/master/source/InfiniteLoader/InfiniteLoader.example.js
along with CellMeasurer for dynamic height:
https://github.com/bvaughn/react-virtualized/blob/master/source/CellMeasurer/CellMeasurer.DynamicHeightList.example.js
The docs for InfiniteLoader.rowCount say:
"Number of rows in list; can be arbitrary high number if actual number is unknown."
So how do you indicate there are no more rows.
If anyone can post an example using setTimeout() to simulate dynamic loaded data, thanks. I can likely get CellMeasurer working from there.
Edit
This doesn't work the way react-virtualized creator says it should or the infinite loading example implies.
Calls:
render(): rowCount = 1
_rowRenderer(index = 0)
_isRowLoaded(index = 0)
_loadMoreRows(startIndex = 0, stopIndex = 0)
_rowRenderer(index = 0)
end
Do I need to specify a batch size or some other props?
class HistoryBrowser extends React.Component
{
constructor(props,context,updater)
{
super(props,context,updater);
this.eventEmitter = new EventEmitter();
this.eventEmitter.extend(this);
this.state = {
history: []
};
this._cache = new Infinite.CellMeasurerCache({
fixedWidth: true,
minHeight: 50
});
this._timeoutIdMap = {};
_.bindAll(this,'_isRowLoaded','_loadMoreRows','_rowRenderer');
}
render()
{
let rowCount = this.state.history.length ? (this.state.history.length + 1) : 1;
return <Infinite.InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={rowCount}
>
{({ onRowsRendered, registerChild }) =>
<Infinite.AutoSizer disableHeight>
{({ width }) =>
<Infinite.List
ref={registerChild}
deferredMeasurementCache={this._cache}
height={200}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowHeight={this._cache.rowHeight}
rowRenderer={this._rowRenderer}
width={width}
/>}
</Infinite.AutoSizer>}
</Infinite.InfiniteLoader>
}
_isRowLoaded({ index }) {
if (index == 0 && !this.state.history.length)
// No data yet, force load
return false;
}
_loadMoreRows({ startIndex, stopIndex }) {
let self = this;
for (let i = startIndex; i <= stopIndex; i++) {
this.state.history[startIndex] = {loading: true};
}
const timeoutId = setTimeout(() => {
delete this._timeoutIdMap[timeoutId];
for (let i = startIndex; i <= stopIndex; i++) {
self.state.history[i] = {loading: false, text: 'Hi ' + i };
}
promiseResolver();
}, 10000);
this._timeoutIdMap[timeoutId] = true;
let promiseResolver;
return new Promise(resolve => {
promiseResolver = resolve;
});
}
_rowRenderer({ index, key, style }) {
let content;
if (index >= this.state.history.length)
return <div>Placeholder</div>
else if (this.state.history[index].loading) {
content = <div>Loading</div>;
} else {
content = (
<div>Loaded</div>
);
}
return (
<Infinite.CellMeasurer
cache={this._cache}
columnIndex={0}
key={key}
rowIndex={index}
>
<div key={key} style={style}>{content}</div>
</Infinite.CellMeasurer>
);
}
}
The recipe you linked to should be a good starting place. The main thing its missing is an implementation of loadNextPage but that varies from app to app based on how your state/data management code works.
Can I trigger first load of data from within InfiniteScroller/List - it seems you need to pass in a data source that is populated with initial page
This is up to you. IMO it generally makes sense to just fetch the first "page" of records without waiting for InfiniteLoader to ask for them- because you know you'll need them. That being said, if you give InfiniteLoader a rowCount of 1 and then return false from isRowLoaded it should request the first page of records. There are tests confirming this behavior in the react-virtualized GitHub.
The docs for InfiniteLoader.rowCount say: "Number of rows in list; can be arbitrary high number if actual number is unknown."
So how do you indicate there are no more rows.
You stop adding +1 to the rowCount, like the markdown file you linked to mentions:
// If there are more items to be loaded then add an extra row to hold a
loading indicator.
const rowCount = hasNextPage
? list.size + 1
: list.size
I can pass values for select, text box and etc but not for multi select. I can update values for multi select. But i can't create a record by passing values for multi select.
This is the code :
$datastring = array(
"gu_action"=> "create",
"recordtype"=>"vendor",
"companyname"=>"Jerald Vend",
'subsidiary'=>1,
'custentity36'=>1
);
custentity36 is multiselect control. It's label is Course
when i pass single value , It works fine.
when i try to pass multiple values for multi select like the below code , i am getting error like "Please enter value(s) for: Course"
$datastring = array(
"gu_action"=> "create",
"recordtype"=>"vendor",
"companyname"=>"Jerald Vend",
'subsidiary'=>1,
'custentity36'=>array(1,3)
);
The Code is : https://gist.githubusercontent.com/ganeshprabhus/a3ebd67712913df3de29/raw/3a6df6a3af8642fceacb3a4b8e519ad96a054e69/ns_script.js
The value you pass is in correct format. In this case the RESTlet code should have the compatibility of handling the multiselect filed. The field set value api that used in the RESTlet should be
nlapiSetFieldValues()
This is the api can be used to set multiselect field value. As per the github refernce you shared. under the create_record function
/********************** Creation *********************************/
function create_record(datain) {
var err = new Object();
// Validate if mandatory record type is set in the request
if (!datain.recordtype) {
err.status = "Failed";
err.message = "Missing recordtype";
return err;
}
var record = nlapiCreateRecord(datain.recordtype);
for ( var fieldname in datain) {
if (datain.hasOwnProperty(fieldname)) {
if (fieldname != 'recordtype' && fieldname != 'id') {
var value = datain[fieldname];
// ignore other type of parameters
if (value && typeof value != 'object') {
record.setFieldValue(fieldname, value);
}
} //recordtype and id checking ends
}
} //for ends
var recordId = nlapiSubmitRecord(record);
nlapiLogExecution('DEBUG', 'id=' + recordId);
var nlobj = nlapiLoadRecord(datain.recordtype, recordId);
return nlobj;
}
The quoted code should be
record.setFieldValues(fieldname,value) // fieldname : custentity36 , value : 1,3
I have a module Search in my ZF2 application. The user fills in a search form out and gets a list of courses.
Now I'm adding the pagination to the module. The paginator is basically working: I can retrieve data over it and the pagination is displayed correctly (pagelinks 1-7 for 70 found courses with the dafault setting 10 items per page).
But it's still not usable. When I click on a pagelink, the form POST data is lost. I know -- it cannot work the way, how I implemented it (see the code below). But I have no idea, how to do it correctly, in order to eep checking the form data and nonetheless be able to use pagination.
That is my code:
Table class Search\Model\CourseTable
class CourseTable {
...
// without pagination
// public function findAllByCriteria(CourseSearchInput $input) {
// with pagination
public function findAllByCriteria(CourseSearchInput $input, $pageNumber) {
...
$select = new Select();
$where = new Where();
$having = new Having();
...
// without pagination
// $resultSet = $this->tableGateway->selectWith($select);
// return $resultSet;
// with pagination
$adapter = new \MyNamespqce\Paginator\Adapter\DbSelect($select, $this->tableGateway->getAdapter());
$paginator = new \Zend\Paginator\Paginator($adapter);
$paginator->setCurrentPageNumber($pageNumber);
return $paginator;
}
...
}
Search\Controller\SearchController
class SearchController extends AbstractActionController {
public function searchCoursesAction() {
$form = $this->getServiceLocator()->get('Search\Form\CourseSearchForm');
$request = $this->getRequest();
if ($request->isPost()) {
$courseSearchInput = new CourseSearchInput();
$form->setInputFilter($courseSearchInput->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$courseSearchInput->exchangeArray($form->getData());
// without pagination
// $courses = $this->getCourseTable()->findAllByCriteria($courseSearchInput);
// with pagination
$page = $this->params()->fromRoute('page');
$paginator = $this->getCourseTable()->findAllByCriteria($courseSearchInput, $page);
} else {
$paginator = null;
}
} else {
$paginator = null;
}
return new ViewModel(array(
'form' => $form,
// without pagination
// 'courses' => $courses,
// with pagination
'paginator' => $paginator,
'cities' => ...
));
}
...
}
How to get it working?
I also have the same problem, and I have solved it. But this is not good way. May be the idea will help you.
I solved it as follow: (Search pagination for Zend tutorial album module)
I build two action in controller named "search" and "index".
Whenever the search form submitted, it always post the value to search action. Search action build the url with search parameters, and redirect to index to disply search result.
And when the pagination links clicked, then posted values are passed through url. So whenever index action ask for search parameters, it always get the values in same format.
I defined route as follows:
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/album[/:action][/:id][/page/:page][/order_by/:order_by][/:order][/search_by/:search_by]',
'constraints' => array(
'action' => '(?!\bpage\b)(?!\border_by\b)(?!\bsearch_by\b)[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
'page' => '[0-9]+',
'order_by' => '[a-zA-Z][a-zA-Z0-9_-]*',
'order' => 'ASC|DESC',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
There is a parameter named "search_by", which will keep all search parameters as a json string. This is the point, which is not good I know, but have not find any other way yet.
"Search" action build this string as -
public function searchAction()
{
$request = $this->getRequest();
$url = 'index';
if ($request->isPost()) {
$formdata = (array) $request->getPost();
$search_data = array();
foreach ($formdata as $key => $value) {
if ($key != 'submit') {
if (!empty($value)) {
$search_data[$key] = $value;
}
}
}
if (!empty($search_data)) {
$search_by = json_encode($search_data);
$url .= '/search_by/' . $search_by;
}
}
$this->redirect()->toUrl($url);
}
And next index action decode the string, do necessary action, and also send the json string to view.
public function indexAction() {
$searchform = new AlbumSearchForm();
$searchform->get('submit')->setValue('Search');
$select = new Select();
$order_by = $this->params()->fromRoute('order_by') ?
$this->params()->fromRoute('order_by') : 'id';
$order = $this->params()->fromRoute('order') ?
$this->params()->fromRoute('order') : Select::ORDER_ASCENDING;
$page = $this->params()->fromRoute('page') ? (int) $this->params()->fromRoute('page') : 1;
$select->order($order_by . ' ' . $order);
$search_by = $this->params()->fromRoute('search_by') ?
$this->params()->fromRoute('search_by') : '';
$where = new \Zend\Db\Sql\Where();
$formdata = array();
if (!empty($search_by)) {
$formdata = (array) json_decode($search_by);
if (!empty($formdata['artist'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('artist', '%' . $formdata['artist'] . '%')
);
}
if (!empty($formdata['title'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('title', '%' . $formdata['title'] . '%')
);
}
}
if (!empty($where)) {
$select->where($where);
}
$album = $this->getAlbumTable()->fetchAll($select);
$totalRecord = $album->count();
$itemsPerPage = 2;
$album->current();
$paginator = new Paginator(new paginatorIterator($album));
$paginator->setCurrentPageNumber($page)
->setItemCountPerPage($itemsPerPage)
->setPageRange(7);
$searchform->setData($formdata);
return new ViewModel(array(
'search_by' => $search_by,
'order_by' => $order_by,
'order' => $order,
'page' => $page,
'paginator' => $paginator,
'pageAction' => 'album',
'form' => $searchform,
'totalRecord' => $totalRecord
));
}
All the sorting and paging url contain that string.
If you know all the searching paarameters before, then you can define that at route, and pass like the same way without json string. As I have to build a common search, I have build a single string.
Source code for "Album search" is available in git hub at https://github.com/tahmina8765/zf2_search_with_pagination_example.
Live Demo: http://zf2pagination.lifencolor.com/public/album
#Sam & #automatix in the question comments are both right. My suggestion (though I'm looking for a simpler alternative) is to construct a segment route, which covers all of the options that you're likely to need and start with a standard form POST request.
Then, after the request is validated, pass the form data to the paginationControl helper as follows:
$resultsView = new ViewModel(array(
'paginator' => $paginator,
'routeParams' => array_filter($form->getData())
));
Then, in your view template, set the route parameters in the paginationControl view helper:
<?php echo $this->paginationControl($paginator, 'Sliding', 'paginator/default',
array('routeParams' => $routeParams)
) ?>
I've used array_filter here because it's a really simple way of removing any element from the form data that's null, empty or so on. That way you don't pass in extra data that you don't need.