priority-web-sdk: startSubForm return the parent form - subform

The following code starts a form and a subform, the issue is the I get the subForm instance of the parent:
await actions.loginTest();
const form = await actions.priority.formStart(this.formName,
onShowMessgeFunc, onUpdateFieldsFunc);
console.log("form", form);
if(form.subForms["SHIPTO2"]) {
const subForm = await form.startSubForm("SHIPTO2");
console.log("subform", subForm);
subForm.choose("CUSTDES", '').then(options => {
let custOptions = options.SearchLine.map(x => {return
{label:x.retval + " - " + x.string1, value: x.retval }});
this.setState({
customersShippingOptions: custOptions,
})
}).catch((err) => {
console.log("CHOOSE ERROR", err);
})
}
Output:

In order to start a subform there must be an active row in the parent form, so the subform will start for that row.
Before you start the subform you should use the getRows() function to retrieve rows and then call setActiveRow(rowIndex) to set an active row. Then you could start the subform.
Modify your code to the following:
await actions.loginTest();
const form = await actions.priority.formStart(this.formName,
onShowMessgeFunc, onUpdateFieldsFunc);
console.log("form", form);
const rows = await form.getRows();
await setActiveRow(5);
if(form.subForms["SHIPTO2"]) {
const subForm = await form.startSubForm("SHIPTO2");
console.log("subform", subForm);
...
}
By the way: I see you're using choose(), Here too there needs to be an active (subform) row,use getRows() with setActiveRow() or create a newRow() before calling choose()

If you want to pull the first line on the screen more than just use the autoRetrieveFirstRows parameter
In the function formStart
Link to the function on the Web SDK site
I personally define this value as default

Related

Repeatedly getting error in Word office add-in "RichApi.Error: Wait until the previous call completes"

After applying Content control on long document (around 10 pages) using office Js, when I am trying to select the content control form a document using below code snippet, I am getting an error
OfficeService.js:338 RichApi.Error: Wait until the previous call completes.
at new n (word-win32-16.01.js:25:246227)
at o.processRequestExecutorResponseMessage (word-win32-16.01.js:25:310053)
at word-win32-16.01.js:25:308456
Sometimes I am able to fetch the selected content control but it's very slow and many times I get the above error. Not sure why this issue is occurring, I have checked in Office Js documentation but couldn't find the resolution.
export const findSelectedContentControl = async () => {
return await Word.run(async (context) => {
try {
const selectedContentControl = context.document.getSelection().contentControls;
selectedContentControl.load("items");
console.log("selectedContentControl..",selectedContentControl)
return await context.sync().then(() => {
let tagArray;
if(selectedContentControl.items.length === 0)
return;
tagArray = selectedContentControl.items[0].tag.split("|");
return tagArray;
});
} catch (error) {
console.log(error);
}
});
};

Office-js Word addin, Grabbing document Text

In my code below, I am attempting to grab the current documents text, set it to a variable and then use that variable in a call. My promise may not be formatted correctly but essentially the getScanResult() function uses the docBodyText variable that I set in handleClickRun(). Everytime I call it the variable is empty. Any idea as to why the document text is not being captured correctly?
const [docBodyText, setDocBodyText] = useState('');
const handleClickRun = async () => {
return Word.run(async (context: Word.RequestContext) => {
const docBody = context.document.body;
docBody.load("text");
await context.sync();
setDocBodyText(docBody.text);
await context.sync();
})
.catch(function (error) {
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
};
const handleScanResults = () => {
new Promise(async (resolve) => {
await handleClickRun();
await getScanResult();
resolve('Completed')
})};
I have tried using the docs and looking for other examples but have not seen any other use cases. The docs I am using is this Perhaps I can be pointed to the correct method.
I have also tried making a variable of just plain text and passing it to my api call and it works perfectly fine, so it is not a call issue.

Forced to re-query element in order to fire click handler

I have a test that where I assert that my API request is supplying the correct params. I am using MSW to mock the api, and I supply a spy for the request handler:
test("supplies order direction descending if header button is clicked twice", async () => {
const mockHandler = jest.fn(handler);
server.use(rest.get(`${config.apiHost}/targets`, mockHandler));
// First request to /targets happens on render
render(<Route>{(props) => <TargetList history={props.history} />}</Route>);
const button = await screen.findByRole("button", { name: "Port" });
// Second request to /targets happens on button click
userEvent.click(button);
await waitFor(() => {
expect(mockHandler).toHaveBeenCalledTimes(2);
});
// Third request to /targets SHOULD happen here but doesn't
userEvent.click(button);
await waitFor(() => {
expect(mockHandler).toHaveBeenCalledTimes(3);
const lastCall = mockHandler.mock.calls[2];
const requestArg = lastCall[0];
expect(requestArg.url.searchParams.get("orderby")).toBe("port");
expect(requestArg.url.searchParams.get("direction")).toBe("descending");
});
});
The above code doesn't work, as firing the click event a second time on the button appears to do nothing. However, if I requery for the button, then I can successfully fire the handler:
test("supplies order direction descending if header button is clicked twice", async () => {
...
const button = await screen.findByRole("button", { name: "Port" });
// Second request to /targets happens on b utton click
userEvent.click(button);
await waitFor(() => {
expect(mockHandler).toHaveBeenCalledTimes(2);
});
// Third request to /targets now works!
const button2 = await screen.findByRole("button", { name: "Port" });
userEvent.click(button2);
await waitFor(() => {
expect(mockHandler).toHaveBeenCalledTimes(3); // SUCCESS!
...
});
});
Why do I have to re-query for the same element? Am I doing something incorrectly?
I do not have complete source code but I would assume you are doing something on your click event handler that would force react to render again and in that case old element would no longer be present on DOM and hence click event handler will not be invoked.
P.S: You can use react life cycle hooks to determine if component was re-rendered.

Protractor: How do I create a function that receives an HTML element as a parameter?

I'm new to Protractor.
I'm trying to select a button based on the button title. I want to make this into a function, and pass the button title in as a parameter.
This is the hard-coded version which works:
it('I click on a button based on the button title', async function() {
let button = element(by.css('button[title=example_button_title]'));
await button.click();
});
I created a global variable and a function to try and replace this, where 'buttonTitle' is the parameter I'm passing into the function:
Variable:
let dynamicButton = buttonTitle => { return element(by.css("'button[title=" + buttonTitle + "]'")) };
Function:
this.selectDynamicButton = async function(buttonTitle) {
await browser.waitForAngularEnabled(false);
await dynamicButton(buttonTitle).click();
};
When I try this I get the following error:
Failed: invalid selector: An invalid or illegal selector was specified
Apologies if there appear to be basic errors here, I am still learning. I appreciate any help that anyone can give me. Thanks.
You can add a custom locator using protractors addLocator functionality. (this is actually a very similar use case to the example listed in the link)
This would look like the following:
onPrepare: function () {
by.addLocator('buttonTitle', function (titleText, opt_parentElement) {
// This function will be serialized as a string and will execute in the
// browser. The first argument is the text for the button. The second
// argument is the parent element, if any.
const using = opt_parentElement || document;
const matchingButtons = using.querySelectorAll(`button[title="${titleText}"]`);
let result = undefined;
if (matchingButtons.length === 0) {
result = null;
} else if (matchingButtons.length === 1) {
result = matchingButtons[0];
} else {
result = matchingButtons;
}
return result;
});
}
This is called like
const firstMatchingButton = element(by.buttonTitle('example_button_title'));
const allMatchingButtons = element.all(by.buttonTitle('example_button_title'));
I had to edit this code before posting so let me know if this does not work. My work here is largely based off this previous answer
let dynamicButton = buttonTitle => { return element(by.css('button[title=${buttonTitle} ]')) };
Use template literals instead of string concatenation with +.
Protractor already has a built in locator which allows you to get a button using the text. I think you are looking at something like that. See the element(by.buttonText('text of button')) locator.
For more reference see here.

Changed values are lost after returning from promise

I am doing E2E Test with protractor, using cucumber for test scenarios.
I face a strange problem: I lost the values I set inside of the promise.
What am I doing wrong?
My step-file.js:
var loggedUser = new User("dummyname", "dummyrole");
this.When(/^I click on a user name$/, function(){
userelem.element(by.className('username')).getText().then(function (txt) {
loggedUser.username = txt;
});
});
this.Then(/^The username of the object "loggedUser" is set to a new value$/, function(){
var answer = "dummyname" != loggedUser.username;
assert.isOk(answer, "username is still dummyname!"); //this line fails since usrname is set back to dummyname again!
});
Thank you #yong for your suggestion on my first post (which I have deleted meanwhile). Finally I understood what you meant. Here is the solution:
(Instead of upgrading to cucumber 2,) I "return" the promise from within the "When-Step", so that protractor waits until it is fullfilled before it executes the next step-function:
var loggedUser = new User("dummyname", "dummyrole");
this.When(/^I click on a user name$/, function(){
return userelem.element(by.className('username')).getText().then(function (txt) {
loggedUser.username = txt;
});
});
Now in the next step-functions, the username is the updated username.
The root cause is you need to make each step definition return a promise, I write a simple code for you and it worked well.
Make step definition return a promise is the key point.
For Cucumber 2:
var { defineSupportCode } = require("cucumber");
defineSupportCode(function({ Given, When, Then }) {
let title = 'test';
Given(/^open npmjs.com$/, function() {
browser.get("http://www.npmjs.com");
return browser.getTitle().then(function(title1){
title = title1;
console.log('title: ' + title);
});
});
Then(/^verify page title$/, function() {
return expect(title).to.equal('npm');
});
});
For Cucumber 1:
module.exports = function() {
let title = 'test';
this.Given(/^open npmjs.com$/, function() {
browser.get("http://www.npmjs.com");
return browser.getTitle().then(function(title1){
title = title1;
console.log('title: ' + title);
});
});
this.Then(/^verify page title$/, function() {
return expect(title).to.equal('npm');
});
};