How to capture dynamic content from element inside an iFrame? - iframe

When I try to capture text from an element inside an iFrame where the element content changes every second, I get "undefined". What might I be doing wrong?
Code:
const { firefox } = require('playwright');
const fs = require('fs');
var url = 'http://jsfiddle.net/6vnam1jr/1/show';
var section_path = 'xpath=/html/body/div/div/div/section';
var iframe_path = 'xpath=/html/body/div/iframe';
var text_path = 'xpath=//html/body/div[2]/div[2]';
async function getElementText(browser,page){
await page.goto(url);
await page.click(section_path);
const frame = await page.$(iframe_path);
const contentFrame = await frame.contentFrame();
await sleep(1000);
let handle = await contentFrame.$eval(text_path);
console.log(handle)
// try again
await sleep(1000);
handle = await contentFrame.$eval(text_path);
console.log(handle)
closeBrowser(browser);
}
async function closeBrowser(browser){
await browser.close();
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
(async () => {
const browser = await firefox.launch({ headless: false });
const page = await browser.newPage();
getElementText(browser,page);
})();```

Thanks for the repro. frame.$eval is an API to run a JS function in the browser which takes the element as an argument.
I believe what you are looking for is an ElementHandle to this element. You can use frame.waitForSelector or frame.$ for this purpose. I have verified that they are not undefined.
// ...
let handle = await contentFrame.waitForSelector(text_path);

Related

#react-native-firebase/storage -How to upload Image and get the image url

I can upload Image but can't get url into firestore.
I have tried many other people's methods without success...
const uploadImage = async () => {
setUploading(true);
const response = await fetch(image.uri)
const blob = await response.blob();
const filename = image.uri.substring(image.uri.lastIndexOf('/') + 1);
**const imgurl = await getDownloadURL().catch((error) => { throw error });**
var ref = a.storage().ref('math/' + filename).child(filename).put(blob);
console.log("Document successfully written!")
try {
await ref;
} catch (e) {
console.log(e);
}
setUploading(false);
Alert.alert(
'sucess'
);
setImage(null);
db.collection("imageurl").doc().set({
url:imgurl,
})
}

waitForResponse does not work in the cluster.tasks callback

I need to open 20 pages parallelly and click on a button then wait for a response after that get the data from a tag. and my code is:
async function getPageData(links) {
return new Promise(async (resolve, reject) => {
try {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_PAGE,
maxConcurrency: 200,
monitor: true,
});
let allData = [];
await cluster.task(async function getData({ page, data: url }) {
await page.goto(url, {
waitUntil: 'networkidle2',
});
const buttonQuery = 'button[role=tab]:first-child';
const buttonElement = await page.waitForSelector(buttonQuery);
await buttonElement.click(buttonElement);
await page.waitForResponse('https://XXX'); // the problem is here
const data = await page.evaluate(getList);
const [oscillators, summary, movingAverage] = data;
allData.push({ oscillators, summary, movingAverage });
});
links.map(async function addQueue(link) {
cluster.queue(link);
});
await cluster.idle();
await cluster.close();
resolve(allData);
} catch (e) {
reject(e);
}
});
but it just work for the first time and ignore the rest of the tasks. but when I remove page.waitForResponse() the all tasks will be run as expected.
How can I make all tasks wait for their response then extract the data?

Playwright waitForFunction for mouse?

I would like to make a screenshot of a hovered button in storybook. My code not working with headless browsers and I probably need to wait some more but can't seem to figure it out. I'm very grateful for any tips.
test('example test', async ({ url }) => {
const browser = firefox.launch({ headless: false, slowMo: 300 });
const page = await (await browser).newPage();
await page.goto(
'url'
);
await page.waitForNavigation({ waitUntil: 'load' });
await page.waitForSelector('#storybook-preview-iframe');
const elementHandle = await page.$('#storybook-preview-iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('button[id=btn]');
const el = await frame.$('button[id=btn]');
const box = await el.boundingBox();
// const watchDog = page.waitForFunction(
// page => {
// page.mouse; ??????????????? I know that there is no mouse.getCurrentPosition method
// },
// {},
// page
// );
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
//await watchDog;
expect(await page.screenshot()).toMatchSnapshot('screenshot.png');
});
You can use page.hover() as this method will scroll view into the selected element then move the cursor over the element.
https://pptr.dev/#?product=Puppeteer&version=v13.5.1&show=api-pagehoverselector
test('example test', async ({ url }) => {
const browser = firefox.launch({ headless: false, slowMo: 300 });
const page = await (await browser).newPage();
await page.goto(
'url'
);
await page.waitForNavigation({ waitUntil: 'load' });
await page.waitForSelector('#storybook-preview-iframe');
const elementHandle = await page.$('#storybook-preview-iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('button[id=btn]');
await frame.hover('button[id=btn]')
expect(await page.screenshot()).toMatchSnapshot('screenshot.png');
});
That's it. Hope this will work.

Not able to assert H1 text

I am trying to write something to check that "About Us" exist on the following page: https://www.aggrowth.com/en-us/about-us and I am just hitting a wall. It shouldn't be difficult, but I have spent too much time on this.
We are using Gherking-testcafe: https://www.npmjs.com/package/gherkin-testcafe
NPM: 6.9.0
TestCafe: 1.0.1
Gherking-Testcafe: 2.0.0
I tried (All below was tested isolation, aka all of the different t.expect was run by themselves):
const h1AboutUs = await Selector('h1');
await t.expect(h1AboutUs.innerText).eql('About Us');
await t.expect(h1AboutUs.innerText).contains('About Us');
await t.expect(h1AboutUs.value).eql('About Us');
await t.expect(Selector('html').textContent).contains('About Us');
and tried removing the await:
const h1AboutUs = Selector('h1');
await t.expect(h1AboutUs.innerText).eql('About Us');
await t.expect(h1AboutUs.innerText).contains('About Us');
await t.expect(h1AboutUs.value).eql('About Us');
await t.expect(Selector('html').textContent).contains('About Us');
It works if I do:
This is the test I have:
When("I see the page load", async t => {
const h1AboutUs = await Selector('h1');
await t.expect(h1AboutUs.visible).eql(true);
await t.hover(h1AboutUs);
await t.expect(h1AboutUs.value).contains('about');
console.log(h1AboutUs.value);
});
My testCafe runner:
const createTestCafe = require('gherkin-testcafe');
const fs = require('fs');
const reportPath = './frontend/src/tests/test-reports'
let testcafe = null;
function readTestCafeConfig() {
configData = fs.readFileSync('.testcaferc.json', 'utf8');
const js = JSON.parse(configData);
return getJSONValues(js, 'src');
};
function getJSONValues(obj, key) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (i === key) {
objects.push(obj[i]);
}
}
return objects;
}
createTestCafe('localhost', 1337, 1338)
.then(tc => {
testcafe = tc;
const runner = testcafe.createRunner();
const src = readTestCafeConfig();
return runner
.src([src])
.browsers('chrome')
.reporter(['spec', {
name: 'json',
output: `${reportPath}/report/report.json`
},
{
name: 'xunit',
output: `${reportPath}/report/report.xml`
}])
// .video(`${reportPath}/videos`, {
// singleFile: true,
// failedOnly: true,
// pathPattern: '${USERAGENT}/${FILE_INDEX}.mp4'
// })
.tags('#aboutUs')
.run();
})
.then(failedCount => {
console.log('Tests failed: ' + failedCount);
testcafe.close();
});
The error I get in the console is:
1) Selector cannot implicitly resolve the test run in context of which it should be executed. If you need to call Selector from the Node.js API callback, pass the test controller manually via
Selector's `.with({ boundTestRun: t })` method first. Note that you cannot execute Selector outside the test code.
Browser: Chrome 74.0.3729 / Mac OS X 10.14.4
12 |});
13 |
14 |When("I see the page load", async t => {
15 | const h1AboutUs = await Selector('h1');
16 |
> 17 | await t.expect(h1AboutUs.visible).eql(true);
18 | await t.hover(h1AboutUs);
19 | await t.expect(h1AboutUs.value).contains('about');
20 | console.log(h1AboutUs.value);
21 |});
22 |
I expect not to see this error msg
You need to implement Selector binding to TestCafe's test controller for such tests. Please have a look at the following example:
const { Given, Then, Before } = require('cucumber');
const { Selector: NativeSelector } = require('testcafe');
const Selector = (input, t) => {
return NativeSelector(input).with({ boundTestRun: t });
};
Before('#aboutHook', async () => {
console.log('Running AGI test.');
});
Given("I am open AGI page", async t => {
await t.navigateTo('https://www.aggrowth.com/en-us/about-us');
});
Then("I should see check about us", async t => {
const h1AboutUs = Selector('h1', t);
//or const h1AboutUs = await Selector('h1', t); if you need
await t
.expect(h1AboutUs.visible).eql(true)
.hover(h1AboutUs);
});
You can get more examples in the gherkin-testcafe repository.
Note also that the h1 element doesn't have a property value.
You can learn more about TestCafe Selectors and their properties in TestCafe Docs.

AWS Lambda: Async Calls outside handler (initialization section, invoke lambda)

I would like to call an asynchronous function outside the lambda handler with by the following code:
var client;
(async () => {
var result = await initSecrets("MyWebApi");
var secret = JSON.parse(result.Payload);
client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET);
});
async function initSecrets(secretName) {
var input = {
"secretName" : secretName
};
var result = await lambda.invoke({
FunctionName: 'getSecrets',
InvocationType: "RequestResponse",
Payload: JSON.stringify(input)
}).promise();
return result;
}
exports.handler = async function (event, context) {
var myReq = await client('Request');
console.log(myReq);
};
The 'client' does not get initialized. The same code works perfectly if executed within the handler.
initSecrets contains a lambda invocation of getSecrets() which calls the AWS SecretsManager
Has anyone an idea how asynchronous functions can be properly called for initialization purpose outside the handler?
Thank you very much for your support.
I ran into a similar issue trying to get next-js to work with aws-serverless-express.
I fixed it by doing the below (using typescript so just ignore the :any type bits)
const appModule = require('./App');
let server: any = undefined;
appModule.then((expressApp: any) => {
server = createServer(expressApp, null, binaryMimeTypes);
});
function waitForServer(event: any, context: any){
setImmediate(() => {
if(!server){
waitForServer(event, context);
}else{
proxy(server, event, context);
}
});
}
exports.handler = (event: any, context: any) => {
if(server){
proxy(server, event, context);
}else{
waitForServer(event, context);
}
}
So for your code maybe something like
var client = undefined;
initSecrets("MyWebApi").then(result => {
var secret = JSON.parse(result.Payload);
client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET)
})
function waitForClient(){
setImmediate(() => {
if(!client ){
waitForClient();
}else{
client('Request')
}
});
}
exports.handler = async function (event, context) {
if(client){
client('Request')
}else{
waitForClient(event, context);
}
};
client is being called before it has initialised; the client var is being "exported" (and called) before the async function would have completed. When you are calling await client() the client would still be undefined.
edit, try something like this
var client = async which => {
var result = await initSecrets("MyWebApi");
var secret = JSON.parse(result.Payload);
let api = new MyWebApiClient(secret.API_KEY, secret.API_SECRET);
return api(which) // assuming api class is returning a promise
}
async function initSecrets(secretName) {
var input = {
"secretName" : secretName
};
var result = await lambda.invoke({
FunctionName: 'getSecrets',
InvocationType: "RequestResponse",
Payload: JSON.stringify(input)
}).promise();
return result;
}
exports.handler = async function (event, context) {
var myReq = await client('Request');
console.log(myReq);
};
This can be also be solved with async/await give Node v8+
You can load your configuration in a module like so...
const fetch = require('node-fetch');
module.exports = async () => {
const config = await fetch('https://cdn.jsdelivr.net/gh/GEOLYTIX/public/z2.json');
return await config.json();
}
Then declare a _config outside the handler by require / executing the config module. Your handler must be an async function. _config will be a promise at first which you must await to resolve into the configuration object.
const _config = require('./config')();
module.exports = async (req, res) => {
const config = await _config;
res.send(config);
}
Ideally you want your initialization code to run during the initialization phase and not the invocation phase of the lambda to minimize cold start times. Synchronous code at module level runs at initialization time and AWS recently added top level await support in node14 and newer lambdas: https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/ . Using this you can make the init phase wait for your async initialization code by using top level await like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
console.log("start init");
await sleep(1000);
console.log("end init");
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
This works great if you are using ES modules. If for some reason you are stuck using commonjs (e.g. because your tooling like jest or ts-node doesn't yet fully support ES modules) then you can make your commonjs module look like an es module by making it export a Promise that waits on your initialization rather than exporting an object. Like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const main = async () => {
console.log("start init");
await sleep(1000);
console.log("end init");
const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
return { handler };
};
# note we aren't exporting main here, but rather the result
# of calling main() which is a promise resolving to {handler}:
module.exports = main();

Resources