Can someone give me an example of the use of SPStatefulLongOperation? It's very poorly documented.
Here's an example of code I've just used. It applies a ThmxTheme (selectedTheme) to all SPWebs in an SPSite (site).
SPStatefulLongOperation.Begin(
"Applying theme to sites.",
"<span id='trailingSpan'></span>",
(op) =>
{
op.Run((opState) =>
{
for (int i = 0; i < site.AllWebs.Count; i++)
{
// Update status.
opState.Status = String.Format(
"<script type='text/javascript'>document.all.item('trailingSpan').innerText = '{0} ({1} of {2})';</script>",
site.AllWebs[i].Title,
i + 1,
site.AllWebs.Count);
// Set the theme.
selectedTheme.ApplyTo(site.AllWebs[i], true);
}
});
op.End(System.Web.HttpContext.Current.Request.UrlReferrer.ToString());
});
Note that the current value of opState.State is appended to the client's HTML (via HttpContext.Current.Response.Write and .Flush) every second. Thus you don't want to send any status message directly; you want to send some JavaScript that will update an existing status element on the page. (Here, the trailingSpan element.)
Related
I have created this script:
function myFunctionNew() {
var threads = GmailApp.search('in:inbox subject:"xxxxxxxxxxxxxx" ');
GmailApp.markThreadsUnread(threads);
for (var i = 0; i < threads.length; i++) {
var mid = threads[i].getId();
GmailApp.getMessageById(mid).markRead();
GmailApp.getMessageById(mid).star();
// ---> so I mark the complete Thread: GmailApp.getUserLabelByName("1").addToThread(threads[i]);
// Here I want to sign only the one eMail with a User label (e.g. Customer)
//
// ---> GmailApp.moveThreadToArchive(threads[i]);
// Here I want to move only the one eMail to the archive
GmailApp.getMessageById(id).forward("xxxxxxxxxx#yyyyyy.com");
}
}
but how I can add a label and move only one eMail which ID I have?
Thanks
Your request can be accomplished with the Gmail API
The Gmail API offers you more options than GmailApp, among others labelling individual messages instead of the whole thread
The Gmail API can be used in Apps Script as Advanced Gmail Service
All you need to do for it is to enable the service by going on Resources > Advanced Google services.... in your Apps Script editor
The specific method you need to add a user label to a single message is Gmail.Users.Messages.modify(resource, userId, id) with the resource {"addLabelIds": [labelId]}
It is important to use for your requests that concern only one message instead of the whole thread the message id, which is different from the thread id.
If you do not know your message id, you need to specify how to find it (e.g. specify the snipept of interest as a search criteria).
It is also important to know that you need to specify the labelId instead of labelName, which for user labels opposed from standard labels are distinct.
If you do not know the label id, you need to loop through all labels by their name until you find the correct one
Below is a sample code showing how to add a label and perform other operations on single messages with a combination of GmailApp and Gmail API
function myFunctionNew() {
var labels = Gmail.Users.Labels.list("me").labels;
for (var a = 0; a < labels.length; a++) {
if(labels[a].name == "Customer"){
var labelId = labels[a].id;
break;
}
}
var threads = GmailApp.search('in:Inbox subject:"xxxxxxxxxxxxxx" ');
for (var i = 0; i < threads.length; i++) {
var mid = threads[i].getId();
//retrieve the thread with Gmail API to obtain the ids of the thread messages correctly
var thread = Gmail.Users.Threads.get("me", mid);
// retrieve all messages and their id for each thread
var messages = thread.messages;
for (var j = 0; j < messages.length; j++) {
var message = messages[j];
//retrieve the message id
var messageId = message.id;
//perform the desired actions with the message id
GmailApp.getMessageById(messageId).markRead();
GmailApp.getMessageById(messageId).star();
//now, you need to chose which messages you want to label, for example selectt by snippet:
var snippet = message.snippet;
if (snippet == "Paste here the snippet"){
var myId = messageId;
//only the messages with the specified snippet will be labelled and forwarded instead of the whole thread:
Gmail.Users.Messages.modify({"addLabelIds": [labelId]}, "me", myId);
GmailApp.getMessageById(myId).forward("xxxxxxxxxx#yyyyyy.com");
}
}
}
}
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 using Selenium IDE to test a web based HR/SW system.
There is a screen that used to enter vacation for employees.
I am having nearly 3000 employee.
I built a test case that enter vacations for one employee using variables.
How can I repeat the test case for all 3000 employees without creating the test case 3000 times. It will take an impossible effort to do that. Note: Each employee is having different vacation data (Type, start date, End date)
Is there any way that I can use a file (Excel,....) that variable can use to read its data from?
Is there any solution for my case???
I will be very very grateful I any one could help me.
Thank you.
You could use an XML file as an input.
1) At first you have to add following user extensions:
sideflow.js - (http://51elliot.blogspot.com/2008/02/selenium-ide-goto.html - additionally this article explains how to add user extension files)
datadriven.js
include.js
At the moment I can't find links where I took the last two, therefore I will give their code in the end of my post. Code is taken from my working directory. You just put the code to correspondent files, then add files to SelemiunIDE user extension and use them.
2) Create an XML file test_data.xml:
<testdata>
<test employee="1" type="1" startDate="01.01.2013" endDate="01.02.2013" />
<test employee="2" type="1" startDate="01.02.2013" endDate="01.03.2013" />
<test employee="3" type="1" startDate="01.03.2013" endDate="01.04.2013" />
...
</testdata>
3) In your test case use code like this:
${DataPath} - full path to a directory with XML-files
<!--BEGIN LOOP-->
<tr>
<td>loadTestData</td>
<td>file://${DataDir}/test_data.xml</td>
<td></td>
</tr>
<tr>
<td>while</td>
<td>!testdata.EOF()</td>
<td></td>
</tr>
<tr>
<td>nextTestData</td>
<td></td>
<td></td>
</tr>
<tr>
<td>echo</td>
<td>employee=${employee} type=${type} ...</td>
<td></td>
</tr>
...
<!--END LOOP-->
<tr>
<td>endWhile</td>
<td></td>
<td></td>
</tr>
Used user extensions:
include.js
/**
* Original Developer: Jerry Qian(qqqiansjtucs#hotmail.com)
* Modified By: John Witchel (jwitchel#colevalleygroup.com)
* include extension for Selenium-IDE edition
* refer to includeCommand_2.1.3 for Selenium-Core edition
* #version 1.3
*
*/
function IDEIncludeCommand() {}
IDEIncludeCommand.LOG_PREFIX = "IDEIncludeCommand: ";
IDEIncludeCommand.BEGIN_TEMPLATE = "begin$Template$";
IDEIncludeCommand.END_TEMPLATE = "end$Template$";
IDEIncludeCommand.VERSION = "1.1";
IDEIncludeCommand.prototype.prepareTestCaseAsText = function(responseAsText, paramsArray) {
/**
* Prepare the HTML to be included in as text into the current testcase-HTML
* Strip all but the testrows (tr)
* Stripped will be:
* - whitespace (also new lines and tabs, so be careful wirt parameters relying on this),
* - comments (xml comments)
* Replace variable according to include-parameters
* note: the include-variables are replaced literally. selenium does it at execution time
* also note: all selenium-variables are available to the included commands, so mostly no include-parameters are necessary
*
* #param responseAsText table to be included as text (string)
* #return testRows array of tr elements (as string!) containing the commands to be included
*
* TODO:
* - selenium already can handle testcase-html. use selenium methods or functions instead
* - find better name for requester
*/
// removing new lines, carret return and tabs from response in order to work with regexp
var pageText = responseAsText.replace(/\r|\n|\t/g,"");
// remove comments
// begin comment, not a dash or if it's a dash it may not be followed by -> repeated, end comment
pageText = pageText.replace(/<!--(?:[^-]|-(?!->))*-->/g,"");
// find the content of the test table = <[spaces]table[char but not >]>....< /[spaces]table[chars but not >]>
var testText = pageText.match(/<\s*table[^>]*>(.*)<\/\s*table[^>]*>/i)[1];
// Replace <td></td> with <td> </td> for iE - credits Chris Astall
// rz: somehow in my IE 7 this is not needed but is not bad as well
testText = testText.replace(/<\s*td[^>]*>\s*<\s*\/td[^>]*>/ig,"<td></td>");// jq: no space
// replace vars with their values in testText
for ( var k = 0 ; k < paramsArray.length ; k++ ) {
var pair = paramsArray[k];
testText = testText.replace(pair[0],pair[1]);
}
// removes all < /tr>
// in order to split on < tr>
testText = testText.replace(/<\/\s*tr[^>]*>/ig,"");
// split on <tr>
var testRows = testText.split(/<\s*tr[^>]*>/i);
return testRows;
};
IDEIncludeCommand.prototype.getIncludeDocumentBySynchronRequest = function(includeUrl) {
/**
* Prepare and do the XMLHttp Request synchronous as selenium should not continue execution meanwhile
*
* note: the XMLHttp requester is returned (instead of e.g. its text) to let the caller decide to use xml or text
*
* selenium-dependency: uses extended String from htmlutils
*
* TODO use Ajax from prototype like this:
* var sjaxRequest = new Ajax.Request(url, {asynchronous:false});
* there is discussion about getting rid of prototype.js in developer forum.
* the ajax impl in xmlutils.js is not active by default in 0.8.2
*
* #param includeUrl URI to the include-document (document has to be from the same domain)
* #return XMLHttp requester after receiving the response
*/
var url = this.prepareUrl(includeUrl);
// the xml http requester to fetch the page to include
var requester = this.newXMLHttpRequest();
if (!requester) {
throw new Error("XMLHttp requester object not initialized");
}
requester.open("GET", url, false); // synchron mode ! (we don't want selenium to go ahead)
try {
requester.send(null);
} catch(e) {
throw new Error("Error while fetching url '" + url + "' details: " + e);
}
if ( requester.status != 200 && requester.status !== 0 ) {
throw new Error("Error while fetching " + url + " server response has status = " + requester.status + ", " + requester.statusText );
}
return requester;
};
IDEIncludeCommand.prototype.prepareUrl = function(includeUrl) {
/** Construct absolute URL to get include document
* using selenium-core handling of urls (see absolutify in htmlutils.js)
*/
var prepareUrl;
// htmlSuite mode of SRC? TODO is there a better way to decide whether in SRC mode?
if (window.location.href.indexOf("selenium-server") >= 0) {
LOG.debug(IDEIncludeCommand.LOG_PREFIX + "we seem to run in SRC, do we?");
preparedUrl = absolutify(includeUrl, htmlTestRunner.controlPanel.getTestSuiteName());
} else {
preparedUrl = absolutify(includeUrl, selenium.browserbot.baseUrl);
}
LOG.debug(IDEIncludeCommand.LOG_PREFIX + "using url to get include '" + preparedUrl + "'");
return preparedUrl;
};
IDEIncludeCommand.prototype.newXMLHttpRequest = function() {
// TODO should be replaced by impl. in prototype.js or xmlextras.js
// but: there is discussion of getting rid of prototype.js
// and: currently xmlextras.js is not activated in testrunner of 0.8.2 release
var requester = 0;
var exception = '';
// see http://developer.apple.com/internet/webcontent/xmlhttpreq.html
// changed order of native and activeX to get it working with native
// xmlhttp in IE 7. credits dhwang
try {
// for IE/ActiveX
if(window.ActiveXObject) {
try {
requester = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e) {
requester = new ActiveXObject("Microsoft.XMLHTTP");
}
}
// Native XMLHttp
else if(window.XMLHttpRequest) {
requester = new XMLHttpRequest();
}
}
catch(e) {
throw new Error("Your browser has to support XMLHttpRequest in order to use include \n" + e);
}
return requester;
};
IDEIncludeCommand.prototype.splitParamStrIntoVariables = function(paramString) {
/**
* Split include Parameters-String into an 2-dim array containing Variable-Name and -Value
*
* selenium-dependency: uses extended String from htmlutils
*
* TODO: write jsunit tests - this could be easy (if there were not the new RegExp)
*
* #param includeParameters string the parameters from include call
* #return new 2-dim Array containing regExpName (to find a matching variablename) and value to be substituted for
*/
var newParamsArray = new Array();
// paramString shall contains a list of var_name=value
var paramListPattern = /([^=,]+=[^=,]*,)*([^=,]+=[^=,]*)/;
if (! paramString || paramString === "") {
return newParamsArray;
} else if (paramString.match( paramListPattern )) {
// parse parameters to fill newParamsArray
var pairs = paramString.split(",");
for ( var i = 0 ; i < pairs.length ; i++ ) {
var pair = pairs[i];
var nameValue = pair.split("=");
//rz: use String.trim from htmlutils.js of selenium to get rid of whitespace in variable-name(s)
var trimmedNameValue = new String(nameValue[0]).trim();
// the pattern to substitute is ${var_name}
var regExpName = new RegExp("\\$\\{" + trimmedNameValue + "\\}", "g");
if (nameValue.length < 3) {
newParamsArray.push(new Array(regExpName,nameValue[1]));
} else {
var varValue = new String(nameValue[1]);
for (var j = 2; j < nameValue.length; j++) {
varValue=varValue.concat("="+nameValue[j]);
}
newParamsArray.push(new Array(regExpName,varValue));
}
}
} else {
throw new Error("Bad format for parameters list : '" + paramString + "'");
}
return newParamsArray;
};
IDEIncludeCommand.prototype.doInclude = function(locator, paramString) {
// Rewrite logic for Selenium IDE by Jerry Qian
var currentSelHtmlTestcase = testCase;
var includeCmdRow = testCase.debugContext.currentCommand();
if (!includeCmdRow) {
throw new Error("IDEIncludeCommand: failed to find include-row in source testtable");
}
var paramsArray = this.splitParamStrIntoVariables(paramString);
var inclDoc = this.getIncludeDocumentBySynchronRequest(locator);
// Get an array of commands from the include text with all whitespace stripped
var includedTestCaseHtml = this.prepareTestCaseAsText(inclDoc.responseText, paramsArray);
this.injectIncludeTestCommands(locator,includeCmdRow,includedTestCaseHtml);
};
IDEIncludeCommand.prototype.injectIncludeTestCommands = function(locator,includeCmdRow, testRows) {
// Rewrite logic for Selenium IDE by Jerry Qian
var newCommands = new Array();
// skip first element as it is empty or <tbody>
for (var i = 1 ; i < testRows.length; i++) {
if(i == 1){// add BEGIN-END block
var beginCommand = new Command(IDEIncludeCommand.BEGIN_TEMPLATE,locator,"");
newCommands.push(beginCommand);
}
var newText = testRows[i];
if(newText.match(/<\s*td.*colspan=.*>(.*)<\/\s*td[^>]*>/i)){//delete comment step
continue;
}
// removes all < /td>
// in order to split on <td>
newText = newText.replace(/<\/\s*td[^>]*>\s*<\/\s*tbody[^>]*>/ig,""); //remove </tbody>first
newText = newText.replace(/<\/\s*td[^>]*>/ig,"");
var newCols = newText.split(/<\s*td[^>]*>/i);
var new_cmd,new_target,new_value;
for (var j = 1 ; j < newCols.length; j++) {//skip 0
if(j == 1) {
new_cmd = newCols[j].replace(/\s/g,"");//trim \s
}else if(j == 2) {
new_target = newCols[j].replace(/\s+$/g,"");//trim end \s
}else if(j == 3) {
new_value = newCols[j].replace(/\s+$/g,"");//trim end \s
}
}
var newCommand = new Command(new_cmd,new_target,new_value);
newCommands.push(newCommand); //correct all steps
}
var endCommand = new Command(IDEIncludeCommand.END_TEMPLATE,locator,"");
newCommands.push(endCommand);//add BEGIN-END block
var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);
var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);
testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection
// Don't inject if it appears the injection has already been done
// (i.e., if the next command is the BEGIN).
if (testCase.commands.length <= testCase.debugContext.debugIndex+1
|| beginCommand.toString() != testCase.commands[testCase.debugContext.debugIndex+1].toString())
{
// The include command cannot be the last command in the TestCase, or else
// the de-injection code in doEnd$Template$ will cause an error. So we'll
// add a simple echo if it is the last.
if (testCase.commands.length == testCase.debugContext.debugIndex+1)
{
// Using concat instead of push so that we don't trigger the TestCase's set-modified flag.
testCase.commands = testCase.commands.concat(new Command("echo", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired.", "The include command cannot be the last line in a TestCase, so this command was added. It can be left in place or removed, as desired."));
}
// This is original code.
var cmsBefore = testCase.commands.slice(0,testCase.debugContext.debugIndex + 1);
var cmdsBehind = testCase.commands.slice(testCase.debugContext.debugIndex + 1, testCase.commands.length);
testCase.commands = cmsBefore.concat(newCommands).concat(cmdsBehind);//Injection
}
};
Selenium.prototype.doInclude = function(locator, paramString) {
LOG.debug(IDEIncludeCommand.LOG_PREFIX + "Version " + IDEIncludeCommand.VERSION);
var ideIncludeCommand = new IDEIncludeCommand();
ideIncludeCommand.doInclude(locator, paramString);
// If goto scripts exist then reindex the labels. goto_sel_ide.js creates an array of labels when the
// script is initialized but an included file's labels are not part of that initial read, so this function
// re-initializes that array with the included files labels (if any). If goto_sel.ide.js is not included
// it's ignored.
try {
this.initialiseLabels();
}
catch (e) {
LOG.debug("Goto Script not used.");
}
};
// Array to hold the starting position of the Begin$Template$ marker. Pushing and popping the position onto an array
// allows us to correctly handle nested includes during clean up.
var beginTemplateIndex = new Array();
// Mark the beginning of the include
Selenium.prototype.doBegin$Template$ = function(locator){
LOG.info("Begin Template " + locator + " at position " + testCase.debugContext.debugIndex);
// Add the current position to the tail of the beginTemplateIndex
beginTemplateIndex.push(testCase.debugContext.debugIndex);
};
// Clean up everything between the closest Begin$Template$ and this $End$Template$, and pop the position off the array.
Selenium.prototype.doEnd$Template$ = function(locator){
// Remove the last Begin$Template$ from the tail of the beginTemplateIndex
var currentBeginTemplateIndex = beginTemplateIndex.pop();
LOG.info("End Template " + locator + " at position " + currentBeginTemplateIndex);
// Delete the commands that we injected in injectIncludeTestCommands.
testCase.commands =
testCase.commands.slice(0,currentBeginTemplateIndex).concat(
testCase.commands.slice(testCase.debugContext.debugIndex+1, testCase.commands.length));
// Set the current command to the next one after the injected block.
testCase.debugContext.debugIndex = currentBeginTemplateIndex-1;
//Must refresh to syncup UI
editor.view.refresh();
};
datadriven.js
/************************************ DATADRIVEN EXTENSION START ********************************************/
/*
NAME:
datadriven
Licensed under Apache License v2
http://www.apache.org/licenses/LICENSE-2.0
PURPOSE:
Basic data driven testing.
Full documentation at http://wiki.openqa.org/display/SEL/datadriven
EXAMPLE USE:
The structure of your data driven test case will be;
-------------------------------------------
COMMAND |TARGET |VALUE
-------------------------------------------
loadTestData |<file path> |
while |!testdata.EOF() |
testcommand1 | |
testcommand...| |
testcommandn | |
endWhile | |
-------------------------------------------
AUTHOR:
Jonathan McBrien
jonathan#mcbrien.org
2008-10-22: v0.1: Initial version.
2009-01-16: v0.2: Updated for Firefox 3.
xmlTestData.prototype.load now uses the include extension's getIncludeDocumentBySynchronRequest method for better portability.
(Why reinvent the wheel? :) - with appreciation to the include extension's authors.)
*/
XML.serialize = function(node) {
if (typeof XMLSerializer != "undefined")
return (new XMLSerializer()).serializeToString(node) ;
else if (node.xml) return node.xml;
else throw "XML.serialize is not supported or can't serialize " + node;
}
function xmlTestData() {
this.xmlDoc = null;
this.testdata = null;
this.index = null;
}
xmlTestData.prototype.load = function(xmlloc) {
loader = new IDEIncludeCommand();
var xmlHttpReq = loader.getIncludeDocumentBySynchronRequest(xmlloc);
this.xmlDoc = xmlHttpReq.responseXML;
this.index = 0;
this.testdata = this.xmlDoc.getElementsByTagName("test");
if (this.testdata == null || this.testdata.length == 0) {
throw new Error("Test data couldn't be loaded or test data was empty.");
}
}
xmlTestData.prototype.EOF = function() {
if (this.index != null && this.index < this.testdata.length) return false;
return true;
}
xmlTestData.prototype.more = function() {
return !this.EOF();
}
xmlTestData.prototype.next = function() {
if (this.EOF()) {
LOG.error("No test data.");
return;
}
LOG.info(XML.serialize(this.testdata[this.index])); // Log should anything go wrong while testing with this data.
if (this.testdata[this.index].attributes.length != this.testdata[0].attributes.length) {
LOG.error("Inconsistent attribute length in test data.");
return;
}
for (i=0; i<this.testdata[this.index].attributes.length; i++){
if (null == this.testdata[0].getAttribute(this.testdata[this.index].attributes[i].nodeName)) {
LOG.error("Inconsistent attribute names in test data.");
return;
}
selenium.doStore(this.testdata[this.index].attributes[i].nodeValue, this.testdata[this.index].attributes[i].nodeName);
}
this.index++;
}
Selenium.prototype.testdata = null;
Selenium.prototype.doLoadTestData = function(xmlloc) {
testdata = new xmlTestData();
testdata.load(xmlloc);
};
Selenium.prototype.doNextTestData = function() {
testdata.next();
};
/************************************ DATADRIVEN EXTENSION END **********************************************/
Current solution is a SelBlocks component which allows you to do cycles according to the dataset in XML
ForXML | XMLFileName.XML
... test case steps
EndForXML
The XML should have specific structure
<testdata>
<vars storedVariable1="xxxxx" storedVariable2="yyyyy" .. storedVariableN="zzzzz" />
.. other record(s)
</testdata>
Each stored variable vill be available in test case as ${storedVariableN}.
You can produce XML by MS Excel or LibreOffice using Formulas like this one and copy it to whole table:
="<vars "&
" "&$A$1&"="""&A2&""" "&
" "&$B$1&"="""&B2&""" "&
..
" "&$X$1&"="""&X2&""" "&
"/>")
XML can be easily copypasted after each change of the dataset.
Selblocks contains the while, datadriven, goto, loop and other extensions mentioned in previous answers. You can use other functionalities of the Selblocks as conditional jumps, conditions, catch-try blocks, functions, etc. See full reference here: http://refactoror.wikia.com/wiki/Selblocks_Reference
Beware with new Selenium 3 release and Sel Core absence it is not guaranteed, that tests with Selblock addon will work in the future versions of the firefox.
You can also check this very nice blog article:
http://seleniumworks.blogspot.com/2014/01/selenium-ide-data-driven.html
This gives an explanation on how to use the needed plugins and where to download them from. It worked perfectly for me.
This answer pretty much has the same approach as the one provided by #Yevgen Ulyanenkov as I used his answer in order to find the above article. But has some more googling to it that hopefully will be helpful to someone.
I think you need to use loop I don't see your code so just check this one for selenium-ide loop If you have any more problem if so comment my answer. I can't comment into your question because reputation is not enough so. If possible please add your code into your question.
I need a way to search via the eventbrite api past events, by organizer, that are private, but I also need to be able to limit the date range. I have not found a viable solution for this search. I assume the organizer_list_events api would be the preferred method, but the request paramaters don't seem to allow for the date range, and I am getting FAR too many returns.
I'm having some similar issues I posted a question to get a response about parsing the timezone, here's the code I'm using to get the dates though and exclude any events before today (unfortunately like you said I'm still getting everything sent to me and paring things out client side)
Note this is an AngularJS control but the code is just using the EventBrite javascript API.
function EventCtrl($http, $scope)
{
$scope.events=[];
$scope.noEventsDisplay = "Loading events...";
Eventbrite({'app_key': "EVC36F6EQZZ4M5DL6S"}, function(eb){
// define a few parameters to pass to the API
// Options are listed here: http://developer.eventbrite.com/doc/organizers/organizer_list_events/
//3877641809
var options = {
'id' : "3588304527",
};
// provide a callback to display the response data:
eb.organizer_list_events( options, function( response ){
validEvents = [];
var now = new Date().getTime();
for(var i = 0; i<response.events.length; i++)
{
var sd = response.events[i].event.start_date;
var ed = response.events[i].event.end_date;
var parsedSD = sd.split(/[:-\s]/);
var parsedED = ed.split(/[:-\s]/);
var startDate = new Date(parsedSD[0], parsedSD[1]-1, parsedSD[2], parsedSD[3], parsedSD[4], parsedSD[5]);
var endDate = new Date(parsedED[0], parsedED[1]-1, parsedED[2], parsedED[3], parsedED[4], parsedED[5]);
if(endDate.getTime()<now)
continue;
response.events[i].event.formattedDate = date.toDateString();
validEvents.push(response.events[i])
}
if(validEvents.length == 0)
{
$scope.$apply(function(scope){scope.noEventsDisplay = "No upcoming events to display, please check back soon.";});
}
else
{
$scope.$apply(function(scope){scope.noEventsDisplay = "";});
}
$scope.$apply(function(scope){scope.events = validEvents;});
//$('.event_list').html(eb.utils.eventList( response, eb.utils.eventListRow ));
});
});
}
I use Shipworks 3 with Magento 1.5.1 and would like to pass the Purchase order NUMBER through. Right now the Payment Type comes through but I need the PO number so it can go on/print on the Packaking slip, invoice, AND Shipping label.
Here's a snippet from the shipworks.php. I'm guessing I just have to add to this section but not sure what to add.
Thank you for any help.
$payment = $order->getPayment();
// CC info
if ($secure)
{
$cc_num = $payment->getCcNumber();
}
else
{
$cc_num = $payment->getCcLast4();
if (!empty($cc_num))
{
$cc_num = '************'.$payment->getCcLast4();
}
}
$cc_year = sprintf('%02u%s', $payment->getCcExpMonth(), substr($payment->getCcExpYear(), 2));
writeStartTag("Payment");
writeElement("Method", Mage::helper('payment')->getMethodInstance($payment->getMethod())->getTitle());
writeStartTag("CreditCard");
writeElement("Type", $payment->getCcType());
writeElement("Owner", $payment->getCcOwner());
writeElement("Number", $cc_num);
writeElement("Expires", $cc_year);
writeCloseTag("CreditCard");
writeCloseTag("Payment");
I was able to use the following to get my po number to show up in the notes. This is what I used. hope it helps.
// CC info
if ($secure)
{
$cc_num = $payment->getCcNumber();
}
else
{
$cc_num = $payment->getCcLast4();
if (!empty($cc_num))
{
$cc_num = '************'.$payment->getCcLast4();
}
}
$cc_year = sprintf('%02u%s', $payment->getCcExpMonth(), substr($payment->getCcExpYear(), 2));
writeStartTag("Payment");
writeElement("Method", Mage::helper('payment')->getMethodInstance($payment->getMethod())->getTitle());
writeStartTag("CreditCard");
writeElement("Type", $payment->getCcType());
writeElement("Owner", $payment->getCcOwner());
writeElement("Number", $cc_num);
writeElement("Expires", $cc_year);
writeCloseTag("CreditCard");
writeCloseTag("Payment");
writeStartTag("Notes");
writeFullElement("Note", $payment->getPoNumber(),array("public" =>"true"));
writeCloseTag("Notes");
WriteOrderItems($order->getAllItems());
WriteOrderTotals($order);
Shipworks is very picky about the XML tags that it receives. You can't just add something to the response because it'll throw errors in the SW software when it tries to sync with your Magento store. You can add it by using an existing element that isn't currently being used. In our case, we weren't using Gift Messages, which get added to the response as Notes, so I re-purposed the Notes element to contain something else.
Look at the WriteOrder function (around line 396) and find this section:
if ($order->getGiftMessageId())
{
$message = Mage::helper('giftmessage/message')->getGiftMessage($order->getGiftMessageId());
$messageString = "Gift message for ". $message['recipient']. ": ". $message['message'];
writeStartTag("Notes");
writeFullElement("Note", $messageString, array("public" => "true"));
writeCloseTag("Notes");
}
Comment out this section and add something like this:
if ($order->getPoNumber())
{
writeStartTag("Notes");
writeFullElement("Note", $order->getPoNumber(), array("public" => "true"));
writeCloseTag("Notes");
}
NOTE: I don't know if $order->getPoNumber() actually works...this is just a sample of what you'll need to do