I have a quite long html menu list (circa 30 menu positions) where I am trying to dynamically populate with labels according to a .csv file. I’d like to change the each menu label i.e. “the Slots”. Each time the page is loaded, I’ll need to look up position1, and return the right label and Title tag in the page I'm pointing to. There are many fixed positions, but when the .csv file is updated, the menu label needs to change accordingly. To add fuel to my fire, I need to hide a menu if the position in the first column of the .csv cannot be found. This absolute amateur would appreciate any insight you might have.
CSV file saved into directory
position1,Slot1
position2,Slot2
position3,Slot3
position4,Slot4
HTML
<a href='link'>
<span class='hidden-minibar'>Slot 1 </span>
</a>
</li>
<a href='gohere'>
<span class='hidden-minibar'>Slot 2 </span>
</a>
</li>
<a href='link'>
<span class='hidden-minibar'>Slot 3 </span>
</a>
</li>
<a href='link'>
<span class='hidden-minibar'>Slot 4 </span>
</a>
</li>
if (window.FileReader) {
var reader = new FileReader();
reader.onload = function(filedata) {
var list = filedata.target.result;
csvParser(list);
};
reader.readAsText(this.selectedfile.target.files.item(0));
}
var csvParser = function(list) {
var quoteRegexp = new RegExp("^\"(.*)\"$");
var data = [];
var lines = list.split(new RegExp("\r?[\r\n]"));
for (var iCtr = 0; iCtr < lines.length; iCtr += 1) {
var fields = lines[iCtr].split(",");
for (var jCtr = 0; jCtr < fields.length; jCtr += 1) {
fields[jCtr] = fields[jCtr].replace(quoteRegexp, "$1");
}
data.push(fields);
}
return data;
}
use FileReader object and read csv file on client site
use csvParser function to parse row csv data
create dynamic html and appropriate logic on csvParser function result data.
Basic csv read Example
http://jsfiddle.net/techrevolt/W8fME/
Related
I've got a page (Angular8) with multiple button/input elements having the same ID.
The ID's are, in the order the appear on the page:
1: for="lastOppDokument-0-VERGE-LEGITIMASJON"
2: for="lastOppDokument-1-VERGE-VERGEMAL"
...
3: for="lastOppDokument-0-VERGE-LEGITIMASJON"
4: for="lastOppDokument-1-VERGE-VERGEMAL"
The elements on lines 1,3 and 2,4 have the same ID.
I'm trying to access the elements using XPath and index like this:
driver.findElement(By.xpath("(//input[#for='lastOppDokument-0-VERGE-LEGITIMASJON'])[1]
driver.findElement(By.xpath("(//input[#for='lastOppDokument-0-VERGE-LEGITIMASJON'])[2]
If I check the size:
int x = driver.findElements(By.xpath("(//input[#for='lastOppDokument-0-VERGE-LEGITIMASJON'])")).size();
it says "2", which is correct.
However, when I try to click those two buttons, the first one (LINE 1) is clicked, but then the next button on the page (LINE 2) i clicked istead of the one on LINE 3.
There's obviously something wrong with my XPath expression, but what? Also, the page is an Angular page, and in the markup code "for" is used to automatically index the ID. But selenium finds the elements (albeit not all the correct ones) using ID. But maybe I need to use something else to identify the correct elements?
UPDATE:
To troubleshoot, I'm skipping the use of a loop and just trying to access the two elements manually:
WebElement buttonToClick = driver.findElement(By.xpath("(//input[#id='lastOppDokument-0-VERGE-LEGITIMASJON'])[1]"));
filUtils.uploadFile(buttonToClick, legitimasjonfil);
WebElement buttonToClick2 = driver.findElement(By.xpath("(//input[#for='lastOppDokument-0-VERGE-LEGITIMASJON'])[2]"));
filUtils.uploadFile(buttonToClick2, legitimasjonfil);
The uploadFile (and related) methods:
public void uploadFile(WebElement id, String filename) {
id.sendKeys(getAbsolutePathToTestFile(TESTFILER_PATH + "/" + filename));
}
private String getAbsolutePathToTestFile(String path) {
return copyFileToTargetTempPath(path, path);
}
private String copyFileToTargetTempPath(String originPath, String destinationPath) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(originPath);
File destination = new File(TARGET_TEMP_PATH + destinationPath);
try {
assert resourceAsStream != null;
org.apache.commons.io.FileUtils.copyInputStreamToFile(resourceAsStream, destination);
} catch (IOException e) {
throw new RuntimeException(e);
}
return destination.getAbsolutePath();
}
UPDATE 2:
<td>
<span class="hb-felt knappesamling-gruppe hb-avstandIngen ng-star-inserted">
<input class="hb-lastOppFil-input hb-bare-skjermleser" type="file" id="lastOppDokument-0-VERGE-LEGITIMASJON"
accept=".pdf,.bmp,.gif,.jpeg,.jpg,.png,.tiff">
<label class="hb-knapp hb-knapp--standard hb-spinner-tekst" for="lastOppDokument-0-VERGE-LEGITIMASJON"> Velg fil </label><!---->
</span>
</td>
<td>
<span class="hb-felt knappesamling-gruppe hb-avstandIngen ng-star-inserted">
<input class="hb-lastOppFil-input hb-bare-skjermleser" type="file" id="lastOppDokument-1-VERGE-VERGEMAL"
accept=".pdf,.bmp,.gif,.jpeg,.jpg,.png,.tiff"><label class="hb-knapp hb-knapp--standard hb-spinner-tekst" for="lastOppDokument-1-VERGE-VERGEMAL"> Velg fil </label><!----></span>
<label class="hb-knapp hb-knapp--standard hb-spinner-tekst" for="lastOppDokument-1-VERGE-VERGEMAL"> Velg fil </label>
</span>
I have a menu that always has the same structure, but the IDs can change from one installation to another. the only thing that stays the same is the heading (in my case "Plugins"). I call the document.getElementsByClassName function with a Selector inside my test:
var slides = Selector(() =>{
return document.getElementsByClassName("c-p-header-text");
});
Every heading of an menu element has the c-p-header-text class. Here is what a menu heading element looks like:
<div id="ext-comp-1002" class="c-p c-tree c-p-collapsed" style="width: auto;">
<div class="c-p-header c-unselectable c-accordion-hd" id="ext-gen135" style="cursor: pointer;">
<div class="c-tool c-tool-toggle" id="ext-gen140"> </div>
<img src="/backEnd/images/s.gif" class="c-p-inline-icon order"><span class="c-p-header-text" id="ext-gen150">Plugins</span>
</div>
It would be easy to use await t.click("#ext-gen150") but it is not safe that it is always this id.
here is what i tried:
await t
.click('#sites__db');
var slides = Selector(() =>{
return document.getElementsByClassName("c-p-header-text");
});
console.log("[DEBUG]" + slides);
console.log("[DEBUG] found " + slides.length + " elements");
for(var i = 0; i < slides.length; i++)
{
var txtOfCurrElem = slides.item(i).innerText;
console.log("[DEBUG "+ i +"] Text: " + txtOfCurrElem);
}
Running this test give me the following output:
[DEBUG]function __$$clientFunction$$() {
var testRun = builder.getBoundTestRun() || _testRunTracker2.default.resolveContextTestRun();
var callsite = (0, _getCallsite.getCallsiteForMethod)(builder.callsiteNames.execution);
var args = [];
// OPTIMIZATION: don't leak `arguments` object.
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}return builder._executeCommand(args, testRun, callsite);
}
[DEBUG] found 0 elements
The plan is to find the element (with the heading "Plugins") and then click on it when the test continuous.
You don't have to use document.getElementsByClassName in this case. You can just use CSS class selector instead:
var slides = Selector('.c-p-header-text');
You should use the count property when dealing with an array of Selectors. docs. Also, element properties, like exists, count, and DOM node state properties are Promisified, so when you use them not in t.expect, you should use the await keyword:
var count = await slides.length;
console.log("[DEBUG] found " + count + " elements");
for(var i = 0; i < count; i++)
{
var txtOfCurrElem = await slides.nth(i).innerText;
console.log("[DEBUG "+ i +"] Text: " + txtOfCurrElem);
}
I found a simple answer to my question. I use the .withText option to click on the Plugins element:
.click(Selector('span').withText("Plugins"))
Since this name is also unique, it is always the correct element that gets clicked. I do not know if it would have worked with the solution from #AndreyBelym if my site is not an extJS web application.
I am trying to convert large HTML file to PDF. but just want to set first page and following page number.
I have used following code
converter = New HtmlToPdf()
Dim file As String = "C:\TEMP\Document5.pdf"
converter.Options.PdfPageSize = PdfPageSize.A4
converter.Options.PdfPageOrientation = PdfPageOrientation.Portrait
converter.Options.MarginTop = 20
converter.Options.MarginBottom = 20
converter.Options.MarginLeft = 10
converter.Options.MarginRight = 10
converter.Options.DisplayFooter = True
Dim doc As PdfDocument = converter.ConvertHtmlString(htmlString)
converter.Footer.TotalPagesOffset =2
converter.Footer.FirstPageNumber = 2
doc.Save(file)
' close pdf document
doc.Close()
but this part not working,
converter.Footer.TotalPagesOffset =2
converter.Footer.FirstPageNumber = 2
and Is there any what to know total pages?
Here's how I handle my page numbering using SelectPDF and ASP.NET MVC Razor.
for (int x = 0; x < PDF.Pages.Count; x++) {
if (x > 0 && x != PDF.Pages.Count - 1) { // will not number first/last page
PdfPage page = PDF.Pages[x];
PdfTemplate customFooter = PDF.AddTemplate(page.PageSize.Width, 33f);
page.DisplayFooter = true;
PdfHtmlElement customHtml = new PdfHtmlElement(domain + "/template/_pagenumber?pageNum=" + x.ToString() + "&totalPages=" + PDF.Pages.Count.ToString());
customFooter.Add(customHtml);
page.CustomFooter = customFooter;
}
}
And here's what my _pagenumber.cshtml file looks like...
<div style="margin-right:48px;margin-left:48px;height:46px;position:relative;top:-4px;z-index:999;">
<div class="row">
<div class="col-xs-6">
<small>Company info goes here</small>
</div>
<div class="col-xs-6 text-right">
<small><strong>Page #(Request.QueryString["pageNum"]) of #(Request.QueryString["totalPages"])</strong></small>
</div>
</div>
</div>
I need to create a screen which automates Google search.
I know JavaScript and I'm trying to get GSE works.
I have a search engine and an API key.
The problem is Google's documentation is cyclic i.e. pages point to each other.
There is no working sample from where I can start my research.
Please help if you know of a working sample.
The documents I have read are:
cselement-devguide
introduction
I know this is an old question, but here is what I did to make the API results formatted like the Google Site Search used to give since they are ending the paid accounts and will have ads now. The API way has an option to pay still for over 100 searches per day, so going with that but had to format the results still, and used the existing one to build the css to do similar styling also.
Search form going to this page is just a simple:
<form action="search-results.htm" id="cse-search-box">
<div>
<input class="" name="q" type="text">
<input class="" type="submit">
</div>
</form>
and then the search results page:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>JSON/Atom Custom Search API Example</title>
<!--<link href="default.css" rel="stylesheet" type="text/css">-->
<link href="google.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="gsc-result-info" id="resInfo-0"></div>
<hr/>
<div id="googleContent"></div>
<script>
//Handler for response from google.
function hndlr(response) {
if (response.items == null) {
//Sometimes there is a strange thing with the results where it says there are 34 results/4 pages, but when you click through to 3 then there is only 30, so page 4 is invalid now.
//So if we get to the invalid one, send them back a page.
window.location.replace("searchresults.htm?start=" + (start - 10) + "&q=" + query);
return;
}
//Search results load time
document.getElementById("resInfo-0").innerHTML = "About " + response.searchInformation.formattedTotalResults + " results (" + response.searchInformation.formattedSearchTime + " seconds)";
//Clear the div first, CMS is inserting a space for some reason.
document.getElementById("googleContent").innerHTML = "";
//Loop through each item in search results
for (var i = 0; i < response.items.length; i++) {
var item = response.items[i];
var content = "";
content += "<div class='gs-webResult gs-result'>" +
"<table class='gsc-table-result'><tbody><tr>";
//Thumbnail image
if (item.pagemap.cse_thumbnail != null)
content += "<td class='gsc-table-cell-thumbnail gsc-thumbnail'><div class='gs-image-box gs-web-image-box gs-web-image-box-portrait'><a class='gs-image' href='" + item.link + "'>" +
"<img class='gs-image' class = 'gs-image-box gs-web-image-box gs-web-image-box-portrait' src='" + item.pagemap.cse_thumbnail[0].src + "'></a></td>";
//Link
content += "<td><a class='gs-title' href='" + item.link + "'>" + item.htmlTitle + "</a><br/>";
//File format for PDF, etc.
if (item.fileFormat != null)
content += "<div class='gs-fileFormat'><span class='gs-fileFormat'>File Format: </span><span class='gs-fileFormatType'>" + item.fileFormat + "</span></div>";
//description text and URL text.
content += item.htmlSnippet.replace('<br>','') + "<br/><div class='gs-bidi-start-align gs-visibleUrl gs-visibleUrl-long' dir='ltr' style='word-break:break-all;'>" + item.htmlFormattedUrl +"</div>" +
"<br/></td></tr></tbody></table></div>";
document.getElementById("googleContent").innerHTML += content;
}
//Page Controls
var totalPages = Math.ceil(response.searchInformation.totalResults / 10);
console.log(totalPages);
var currentPage = Math.floor(start / 10 + 1);
console.log(currentPage);
var pageControls = "<div class='gsc-results'><div class='gsc-cursor-box gs-bidi-start-align' dir='ltr'><div class='gsc-cursor'>";
//Page change controls, 10 max.
for (var x = 1; x <= totalPages && x<=10; x++) {
pageControls += "<div class='gsc-cursor-page";
if (x === currentPage)
pageControls += " gsc-cursor-current-page";
var pageLinkStart = x * 10 - 9;
pageControls+="'><a href='search-results.htm?start="+pageLinkStart+"&q="+query+"'>"+x+"</a></div>";
}
pageControls += "</div></div></div>";
document.getElementById("googleContent").innerHTML += pageControls;
}
//Get search text from query string.
var query = document.URL.substr(document.URL.indexOf("q=") + 2);
var start = document.URL.substr(document.URL.indexOf("start=") + 6, 2);
if (start === "1&" || document.URL.indexOf("start=") === -1)
start = 1;
//Load the script src dynamically to load script with query to call.
// DOM: Create the script element
var jsElm = document.createElement("script");
// set the type attribute
jsElm.type = "application/javascript";
// make the script element load file
jsElm.src = "https://www.googleapis.com/customsearch/v1?key=yourApikeyhere&cx=yoursearchengineidhere&start="+start+"&q=" +query +"&callback=hndlr";
// finally insert the element to the body element in order to load the script
document.body.appendChild(jsElm);
</script>
</body>
</html>
I am using Dojo v1.8 with Worklight; I would like create a SwapView dynamically in View, but I'm encountering problems...
HTML code:
<div data-dojo-type="dojox.mobile.View" id="listeInscriptionView"
data-dojo-props="selected:false,scrollDir:'vh'"
style="background-image: url('images/bg-texture.jpg');"
data-dojo-id="id">
</div>
JavaScript code:
var view = registry.byId(listeInscriptionView);
alert(view);
for(var i = 1; i < 3; i++ ){
var swap = new dojox.mobile.SwapView({
id: i,
selected:false
});
var head = new dojox.mobile.Heading({
label:'Swap' + i,
});
swap.addChield(head);
view.addChield(swap);
alert("test" + i);
}
The above does not work. How I can create the widget SwapView dynamically?
Is that a copy of your actual code? I have not actually tested it but there are syntax errors and typos:
registry.byId(listeInscriptionView);
should be
registry.byId("listeInscriptionView");
(missing quotes), and
swap.addChield(head);
view.addChield(swap);
should be
swap.addChild(head);
view.addChild(swap);
Maybe it works better with these errors fixed?