JS WebdriverIO TypeError: browser.getMetrics is not a function - google-chrome-devtools

I'm trying to reproduce an example site https://webdriver.io/docs/devtools-service, but i get an error "TypeError: browser.getMetrics is not a function"
const assert = require('assert')
describe('JSON.org page', () => {
before(() => {
browser.enablePerformanceAudits()
})
it('should load within performance budget', () => {
/**
* this page load will take a bit longer as the DevTools service will
* capture all metrics in the background
*/
browser.url('http://json.org')
let metrics = browser.getMetrics()
assert.ok(metrics.speedIndex < 1500) // check that speedIndex is below 1.5ms
let score = browser.getPerformanceScore() // get Lighthouse Performance score
assert.ok(score >= .99) // Lighthouse Performance score is at 99% or higher
$('=Esperanto').click()
metrics = browser.getMetrics()
assert.ok(metrics.speedIndex < 1500)
score = browser.getPerformanceScore()
assert.ok(score >= .99)
})
after(() => {
browser.disablePerformanceAudits()
})
})
My package.json file:
{
"name": "devtools",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "wdio"
},
"author": "",
"license": "ISC",
"devDependencies": {
"#wdio/cli": "^7.20.7",
"#wdio/devtools-service": "^7.20.7",
"#wdio/local-runner": "^7.20.7",
"#wdio/mocha-framework": "^7.20.7",
"#wdio/spec-reporter": "^7.20.7",
"chromedriver": "^103.0.0",
"wdio-chromedriver-service": "^7.3.2"
},
"dependencies": {
"uuid": "^8.3.2"
}
}
My wdio.conf.js file:
services: ['chromedriver','devtools'],
but i get an error in Terminal:
[0-0] TypeError in "JSON.org page.should load within performance budget"
TypeError: browser.getMetrics is not a function
at Context.<anonymous> (C:\Users\User\PhpstormProjects\DevTools\test\specs\1.js:15:31)
at Context.executeAsync (C:\Users\User\PhpstormProjects\DevTools\node_modules\#wdio\utils\build\shim.js:333:27)
at Context.testFrameworkFnWrapper (C:\Users\User\PhpstormProjects\DevTools\node_modules\#wdio\utils\build\test-framework\testFnWrapper.js:51:32)

The problem was in asynchronous mode, did this:
const assert = require('assert')
describe('JSON.org page', async () => {
before(() => {
browser.enablePerformanceAudits()
})
it('should load within performance budget', async () => {
/**
* this page load will take a bit longer as the DevTools service will
* capture all metrics in the background
*/
await browser.url('http://json.org')
let metrics = browser.getMetrics()
assert.ok((await metrics).speedIndex < 1500) // check that speedIndex is below 1.5ms
let score = await browser.getPerformanceScore() // get Lighthouse Performance score
assert.ok(score >= .90) // Lighthouse Performance score is at 99% or higher
})
after(async () => {
await browser.disablePerformanceAudits()
})
})

Related

next js redirects not working in production page

const withPlugins = require("next-compose-plugins")
const withBundleAnalyzer = require("#next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
})
/**
* #type {import('next').NextConfig}
*/
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/",
destination: "/schedule/calendar",
permanent: false,
},
]
},
exportPathMap: async function () {
return {
"/": { page: "/schedule/calendar" },
}
},
}
const withTM = require("next-transpile-modules")([
"#fullcalendar/common",
"#fullcalendar/daygrid",
"#fullcalendar/interaction",
"#fullcalendar/react",
"#fullcalendar/timegrid",
])
module.exports = withPlugins([[withBundleAnalyzer({ nextConfig, target: "serverless" })], [withTM]], nextConfig)
This is my next.config.js file. In development page this working well, but in production page when i enter root page like 'example.com' show the /schedule/calendar page. Also i try to enter 'example.com/schedule/calendar' show the error This XML file does not appear to have any style information associated with it. I check aws s3 buckets i can't see schedule.html. How can i solve this problem

race problem with mocha unit testing Firestore onSnapshot, test ends before onSnapshot returns data

I have a function I'm testing. It's called from the constructor on an object.
constructor(authorID: string) {
this.loadRecipes(authorID)
}
private loadRecipes = (author_id: string) => {
const first = query(collection(db, "cookbook"), where("author_id", "==", author_id));
this._unsubscribe = onSnapshot(first, (querySnapshot) => {
this._recipes = [];
querySnapshot.forEach((doc) => {
this._recipes.push(new Recipe(doc.id, doc.data().author_id, "", "", -1, "draft", [], [], [], 0, ""));
});
});
};
I'm calling it from a mocha test:
it("Creates a recipe list by author ID", () => {
authorRecipes = new RecipeList(author_id);
assert(authorRecipes.list.length>0);
});
The code works fine, but the test fails. The problem is that the test completes running long before the onSnapshot ever fires and populates the list. Is there a way to force the query to populate - sort of like an async/await? Or a way to force mocha to wait? Setting a breakpoint & debugging, the onSnapshot is eventually called so the code works. I just cannot do any follow on tests because the data isn't there yet.
I ended up adding a "loading" flag to the class:
private loadRecipes = (author_id: string, _max?: number, _start?: number) => {
this._loading = true;
const first = query(collection(db, "cookbook"), where("author_id", "==", author_id));
this._unsubscribe = onSnapshot(first, (querySnapshot) => {
this._recipes = [];
querySnapshot.forEach((doc) => {
this._recipes.push(new Recipe(doc.id, doc.data().author_id, "", "", -1, "draft", [], [], [], 0, ""));
});
this._loading = false;
});
};
And then watched the flag with a delay in the test:
it("Creates a recipe list by author ID", async () => {
authorRecipes = new RecipeList(author_id);
while (authorRecipes.loading) {
await timeout(1000);
}
assert(!authorRecipes.loading);
});
Not super elegant, but gets the job done.

express-session setting new session every time, and does not persist after creating the session

I am using express for my backend on localhost:8080
Using react for my frontend on localhost:3000
No proxy is in use. Just simple http://localhost:3000/roster sending a request to http://localhost:8080/
I have set up all the cross origin and header stuff for cors() too
my postgres store is setup, and when I query the sessions table, I can see the session data there.
using axios for fetch on my front end, and I have configured credentials:true there also.
I am console logging my sessionID in the middleware (before its been set) and then I log it again in the request I am using to test my sessions. In the middleware it's unidentified as expected, and then once I log it in the request, I get a real session id as expected. Everything seems to work, but when I send the request for a second time, it send back a whole new sessionID instead of persisting.
As I looked further it said it could have something to do with the cookie not being set? But I have looks all over for configuring the setting correctly like resave and httpOnly, and all those seem to be okay too.
What am I doing wrong, how can I fix this? I have included my package.json for client and server here first....
PACKAGE.JSON CLIENT
{
"name": "client.react",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.21.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "0.9.5"
},
"devDependencies": {},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
PACKAGE.JSON SERVER
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"connect-pg-simple": "^6.2.1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-session": "^1.17.1",
"jsonwebtoken": "^8.5.1",
"pg": "^8.5.1",
"uuid": "^8.3.2"
}
}
CLIENT
/**
* File: /src/pages/roster.js
* Date: 01-28-2021
* Author: jreyes
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | initialization
* ---------------------------------------------------------------------------------------
*/
import React from 'react';
import Axios from 'axios';
const Roster = () => {
const [roster, setRoster] = React.useState([]);
/** Fetch the roster. */
const handleRoster = () => {
Axios.get("http://localhost:8080/", {
headers: {
"Content-Type":"application/json"
}
}, { withCredentials: true })
.then((response) => {
console.log(response.data);
})
}
return (
<React.Fragment>
<h1>Roster</h1>
<button onClick={handleRoster}>Get Roster</button>
</React.Fragment>
)
}
export default Roster;
CLIENT CONSOLE LOG FROM CHROME
Notice the two different sessionIDs sent back from the server. From the same roster page and I just click the button once, and then a second time.
{hit: "!/", session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"}
hit: "!/"
session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"
__proto__: Object
roster.js:25
{hit: "!/", session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"}
hit: "!/"
session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"
__proto__: Object
SERVER
/**
* File: index.js
* Date: 01-20-2021
* Author: Bennm23
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-20-2021 | benm23 | initialization
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | formatted code; fixed cors; added env
* | | functionality; db is outsourced in db.js;
* ---------------------------------------------------------------------------------------
* 01-30-2021 | jreyes | added express sessions; uuid for unique strings;
* | | added request to fetch user profile.
* ---------------------------------------------------------------------------------------
*/
require('dotenv').config();
const express = require("express");
const app = express();
const db = require('./db');
const bcrypt = require("bcryptjs");
const cors = require("cors");
const {v4: uuidv4} = require('uuid');
const session = require('express-session');
const pgSession = require('connect-pg-simple')(session);
app.use(express.json());
app.use(express.urlencoded());
app.use(session({
genid: (req) => {
console.log("Inside middleware, not set yet: ");
console.log(req.sessionID);
return uuidv4();
},
store: new pgSession({
pool: db,
tableName: "session"
}),
secret: process.env.ES_SECRET,
cookie:{
maxAge:36000,
httpOnly: false,
secure: false
},
resave: false,
saveUninitialized: true
}));
app.use(cors({
origin: "http://localhost:3000",
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}));
/** Set proper headers */
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
});
/**
* Test request for sessions.
*/
app.get("/", (req, res) => {
console.log('Hit detected: defualt home route');
console.log('Session detected: ' + req.sessionID);
res.json({
"hit":"!/",
"session": "SessionID: " + req.sessionID
});
});
/**
* Register a new user.
*
* Errors:
* !email: email has been taken
* !register: database malfunction
* !hashing: hashing malfunction
*
* */
app.post("/signup", async (req, res) => {
const userExistsReq = "SELECT * FROM users WHERE email = $1";
const userExistsRes = await db.query(userExistsReq,[req.body.email]);
// Email already exists in the database.
if(userExistsRes.rowCount > 0){
res.status(200);
res.json({"error":"!email"});
}
else{
try {
// Hash password.
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(req.body.password, salt)
const registerTemplate = "INSERT INTO users (email, password, firstname, lastname) VALUES ($1,$2,$3,$4)";
// Add user to database.
try {
const registerRes = await db.query(registerTemplate,
[
req.body.email,
hashedPassword,
req.body.firstname,
req.body.lastname
]
);
res.status(201);
res.json({"good":"register"});
// Error adding user.
} catch (err) {
res.status(500);
console.error("error: " + err);
res.json({"error":"!register"});
}
}
// Error hashing password.
catch {
res.status(500);
console.error("error: " + err)
res.json({"error":"!hashing"});
}
}
});
/**
* Login an existing user.
*
* Errors:
*
* !email: user does not exist
* !password: user entered wrong password
* !login: database malfunction.
*/
app.post("/login", async (req, res) => {
// Verify user presence in db.
const userExistsReq = "SELECT * FROM users WHERE email = $1";
const userExistsRes = await db.query(userExistsReq, [req.body.email]);
// User does not exits.
if(userExistsRes.rowCount == 0){
res.status(200);
res.json({"error":"!email"});
}
else{
// Test user credentials.
try {
if(await bcrypt.compare(req.body.password, userExistsRes.rows[0].password)){
const email = userExistsRes.rows[0].email;
const firstname = userExistsRes.rows[0].firstname;
res.status(200);
res.json({"good":"login"})
}else{
res.status(200);
res.json({"error":"!password"})
}
// Error finding user.
} catch (err) {
res.status(200);
console.error("Error while running: " + err);
res.json({"error":"!login"});
}
}
});
/**
* Fetch the roster of players.
*
* !roster: database malfunction
*/
app.get("/roster", async (req, res) => {
const fetchRosterTemplate = "SELECT * FROM users";
const response = await db.query(fetchRosterTemplate);
if (response.rowCount == 0) {
res.status(200);
res.json({"error":"!roster"});
} else {
res.status(200);
res.json(response.rows);
}
});
/**
* Start server.
*/
app.set("port", 8080);
app.listen(app.get("port"), () => {
console.log(`Find the server at http://localhost:${ app.get("port") }`);
});
SERVER CONSOLE LOG
This is the console after two requests from the roster page in my client. I click the button twice and these are the two things that are logged.
jreyes#x1carbon:~/Projects/mothers-rfc/server$ node index.js
body-parser deprecated undefined extended: provide extended option index.js:29:17
Find the server at http://localhost:8080
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: db9af88c-0101-4bf5-82c7-f57fbe9dac1d
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: b1a5ffd2-c986-4932-827c-a6ce644a0b3e
Setting httpOnly solved my issue. I had it set to false and it needs to be true. I left the secure option for cookies set to false.
httpOnly: true
solved my problem :)

How can i set multiCapabilities dynamically in protractor config file

I am using protractor 5.2.2. I have a requirement of setting multiCapabilities dynamically in protractor config file.Currently i have hard coded and set multiCapabilities as given below.
multiCapabilities: [
{
browserName: 'chrome',
BatchNo:1
},
{
browserName: 'chrome',
BatchNo:2
}],
i have a dynamic parameter called threads in beforeLaunch function.So depending on the value of this parameter, i have to set multiCapabilities dynamically and the BatchNo also.In above code i have threads=2, so i have 2 objects in multiCapabilities and BatchNo set as 1 and 2 respectively.If i have threads=4 in beforeLaunch function, then i have to set 4 objects in multiCapabilities and BatchNo should set as 1,2,3 and 4 respectively(i am using chrome browser for all threads).How can i do this.Thanks in advance.
We can use getMultiCapabilities() to customize dynamical capabilites.
/**
* If you need to resolve multiCapabilities asynchronously (i.e. wait for
* server/proxy, set firefox profile, etc), you can specify a function here
* which will return either `multiCapabilities` or a promise to
* `multiCapabilities`.
*
* If this returns a promise, it is resolved immediately after
* `beforeLaunch` is run, and before any driver is set up. If this is
* specified, both capabilities and multiCapabilities will be ignored.
*/
getMultiCapabilities?: any;
Define a function to get thread value.
let getThreadValue = function () {
return new Promise(function (resolve, reject) {
request = new Request("sql to query thread value", function (err, rowCount, rows) {
if (err) {
reject(err);
}
else {
resolve('put thread value at here');
}
});
connection.execSql(request);
});
};
Use getMultiCapabilities in protractor conf.js:
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['./test.js'],
// If getMultiCapabilities is specified,
// both capabilities and multiCapabilities will be ignored
getMultiCapabilities: function () {
return getThreadValue().then(function (thread) {
let multiCapabilities = [];
for (index = 1; index <= thread; index++) {
multiCapabilities.push({
browserName: 'chrome',
BatchNo: index
})
}
return multiCapabilities;
});
}
};
Related code for further question about beforeLaunch issue:
let getThreadValue = function () {
return new Promise(function (resolve, reject) {
connection.on('connect', function (err) {
if (err) {
reject(err);
}
else {
request = new Request("select * from location", function (err, rowCount, rows) {
if (err) {
reject(err);
} else {
resolve(Math.ceil(rowCount / 3));
}
});
connection.execSql(request);
}
});
});
};
beforeLaunch: function() {
return getThreadValue().then(function (thread) {
console.log('thread: ' + thread);
return new Promise(function(resolve, reject){
connection.on('connect', function (err) {
if (err) {
reject(err);
} else {
request = new Request("EXEC [usp_GetPostDetails] 1514," + thread, function (err, rowCount, rows) {
if (err) {
reject(err);
} else {
console.log("done");
resolve('done');
}
});
connection.execSql(request);
}
});
});
});
}
multiCapabilities should get Array<string>. You could create a variable that will have a function that returns specific array corresponding to your condition.
For example:
firstly create a function that create your own multiCapabilities array
function createArray(threads) {
const array = [];
for (let batch = 1; batch <= threads; batch++) {
array.push({
browserName: 'chrome',
BatchNo: batch
});
}
return array;
}
create variable that returns specific multiCapabilities corresponding to your threads
const myMultiCapabilities = (threads) => {
return createArray(threads);
}
and finally use it for setting multiCapabilities:
multiCapabilities: myMultiCapabilities(threads)

How to invoke openwhisk action within openwhisk platform on bluemix?

I have created two actions on OpenWhisk on Bluemix. Both independently work fine when I can call them from outside the OpenWhisk platform. But I want to call action1 from within action2, and am using the following syntax:
var openwhisk = require('openwhisk');
function main(args){
const name = 'action2';
const blocking = true;
const params = { param1: 'sthing'};
var ow = openwhisk();
ow.actions.invoke({name, blocking, params})
.then(result => {
console.log('result: ', result);
return result; // ?
}).catch(err => {
console.error('failed to invoke actions', err);
});
}
But I get an empty result and no console messages. Some help would be great.
Update1:
When adding as suggested the return option, to return the Promise of OpenWhisk, as follows:
return ow.actions.invoke({name, blocking, params})
.then(result => {
console.log('result: ', result);
return result;
}).catch(err => {
console.error('failed to invoke actions', err);
throw err;
});
the response value of action2 is not as expected but contains:
{ "isFulfilled": false, "isRejected": false }
where I expect the return message of action2 (which reads a Google Sheets API) and parses the result:
{
"duration": 139,
"name": "getEventCfps",
"subject": "me#email.com",
...
"response": {
"result": {
"message": [
{
"location": "Atlanta, GA",
"url": "https://werise.tech/",
"event": "We RISE Women in Tech Conference",
"cfp-deadline": "3/31/2017",
...
}
]
},
"success": true,
"status": "success"
},
...
}
So I am expecting I am not parsing the '.then(result' variable in action1 correctly? cause when I test action2 separately, from outside OpenWhisk via Postman or API Connect, or directly by 'Run this action' in OpenWhisk/Bluemix it returns the correct values.
Update2:
Alright solved. I was calling the ow.actions.invoke to action2 in a function that was called within the action1, this nesting of returns, caused the issue. When I moved the invoke code directly in the main function, all resolved as expected. Double trouble when nesting promises and returns. Mea culpa. Thanks everyone
You need to return a Promise in your function try this
var openwhisk = require('openwhisk');
function main(args){
const name = '/whisk.system/utils/echo';
const blocking = true;
const params = { param1: 'sthing'};
var ow = openwhisk();
return ow.actions.invoke({name, blocking, params})
.then(result => {
console.log('result: ', result);
return result;
}).catch(err => {
console.error('failed to invoke actions', err);
throw err;
});
}
If you just want to invoke the action:
var openwhisk = require('openwhisk');
function main(args) {
var ow = openwhisk();
const name = args.action;
const blocking = false
const result = false
const params = args;
ow.actions.invoke({
name,
blocking,
result,
params
});
return {
statusCode: 200,
body: 'Action ' + name + ' invoked successfully'
};
}
If you want to wait for the result of the invoked action:
var openwhisk = require('openwhisk');
function main(args) {
var ow = openwhisk();
const name = args.action;
const blocking = false
const result = false
const params = args;
return ow.actions.invoke({
name,
blocking,
result,
params
}).then(function (res) {
return {
statusCode: 200,
body: res
};
});
}