I am working on test automation using Microsoft Playwright. I am trying to implement POM design pattern. I am getting "Cannot read property '$' of undefined" error while trying to run my code.
My Page Object class:
import { Page } from "playwright";
export default class LoginPage {
private page
constructor(page: Page){
this.page = page;
}
userNameField = async () => await this.page.$("input[name = 'username']");
passwordField = async () => await this.page.$("input[name = 'password']");
loginButton = async () => await this.page.$("input[value = 'Log In']");
public async enterUserName(name: string){
const username = await this.userNameField();
await username?.fill(name);
}
public async enterPassword(password:string){
const pass = await this.passwordField();
await pass?.fill(password);
}
public async clickOnLogin(){
const login = await this.loginButton();
await login?.click();
}
}
My Test class:
import { Browser, chromium, Page } from "playwright";
import LoginPage from "../PageObjects/login.page";
import fs = require('fs');
const yaml = require('js-yaml');
let fileContents = fs.readFileSync('C:/Users/diwakar.devapalan/Documents/Learn/AutomationProjects/Playwright/playwright-jest-typescript/config.yaml', 'utf8');
let data = yaml.safeLoad(fileContents);
let page: Page;
let browser:Browser;
describe('Login', () => {
let login: LoginPage;
beforeAll( async () => {
login = new LoginPage(page);
})
beforeEach( async () => {
browser = await chromium.launch({
headless: false
})
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(data.url);
})
test('Successful Login', async () => {
await login.enterUserName(data.username);
await login.enterPassword(data.password);
await login.clickOnLogin();
})
afterEach( async () => {
await browser?.close();
})
})
Link to the github page.
Can someone please help me with this error. Thanks in advance.
Related
From the docs, this is how you fetch data, but since it's outside the function, how do I track the loading state so users can know when a data section is loading.
async function getData() {
const res = await fetch('https://api.example.com/...');
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
You can use custom hooks for that:
useData.js
async function useData() {
const [loading, setLoading] = useState(true)
cost [error, setError] = useState(false)
const [data, setData] = useState()
useEffect(() => {
const fetchData = async () => {
setLoading(true)
const res = await fetch('https://api.example.com/...');
if (!res.ok) {
setError(true)
return
}
setError(false)
setLoading(false)
setData(res.json())
}
fetchData()
}, [])
return {loading, error, data}
}
//page
export default function Page() {
const {data, loading, error} = useData();
if(loading){ return <p>Loading...</p>}
if(error){ return <p>Error...</p>}
if(!data){ return <p>No data!!!</p>}
return <main></main>;
}
Later, you can set up your hook to use an uri param so you can reuse this hook in multiple pages.
In my social media app in Home page i want to dispatch 3 actions from my api:
posts , users , userDetails
But this may show an error(500) on vercel because the request takes a lot of time to get all these data.
vercel Error
this error will not appear again after refreshing the page !!!
i think that's because the request takes a lot of time to get all the data.
-> getServersideProps Code
export const getServerSideProps = wrapper.getServerSideProps(
store => async (context) =>
{
const {req} = context
const session = await getSession({ req });
await store.dispatch(fetchPostsAction());
await store.dispatch(fetchUsersAction(4));
await store.dispatch(LoggedInUserAction({email:session.user.email}));
})
-> fetchPostsAction Code
"post/list",
async (_, { rejectWithValue, getState, dispatch }) => {
try
{
let link = `${URL}/api/posts`;
const { data } = await axios.get(link,{
headers: { "Accept-Encoding": "gzip,deflate,compress" }
});
console.log("#2 got the data",data)
return data;
} catch (error) {
if (!error?.response) throw error;
return rejectWithValue(error?.response?.data);
}
}
);
-> extraReducer Code
builder.addCase(createpostAction.pending, (state, action) => {
state.createPostLoading = true;
});
builder.addCase(createpostAction.fulfilled, (state, action) => {
state.postLists = [...state.postLists, action.payload.post].sort((a, b) => b.createdAt > a.createdAt ? 1 : -1)
state.createPostLoading = false;
state.isCreated = true;
state.appErr = null;
state.serverErr = null;
});
builder.addCase(createpostAction.rejected, (state, action) => {
state.createPostLoading = false;
state.appErr =
action?.payload?.message || action?.payload?.error?.message;
state.serverErr = action?.error?.message;
});
-> get posts from api Code
handler.get(async (req, res) =>
{
await db.connect();
try {
const posts = await Post.find().populate({
path: 'user',
model: 'User',
}).populate({
path:'comments',
options: {sort: {'createdAt' : -1} }
}).sort('-createdAt')
res.status(200).json({
success:true,
posts
});
} catch (err) {
res.status(500).json(err.message)
}
await db.disconnect();
})
so what is the best way to fetch all these data in next js ?
I hope there is a way to solve this problem
I'm playing around with nextjs
and I've trouble to debug a function like
this:
export const authInitialProps = isProtectedRoute => {
console.log("mylog");// this works
return ({ req, res }) => {
console.log("inner my log", req); // this doesn't work
};
};
Using in a page
ProfilePage.getInitialProps = async () => {
const auth = authInitialProps(true);
if (!typeof auth === "function") {
const user = await getUserProfile();
return { user };
}
return { user: null };
};
I never see "inner my log" both in chrome console
and in my console terminal.
What's the problem please?
Try this It may work:
ProfilePage.getInitialProps = async () => {
const auth = await authInitialProps(true); // await added
if (!typeof auth === "function") {
const user = await getUserProfile();
return { user };
}
return { user: null };
};
I think it should be async to not pass over it!
I want to upload an image to firebase (which is working), then return the download URL of the image and store it as a string.
Here is my code:
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
firebase.storage().ref().child(imageName).put(blob)
.then(snap => {
return snap.ref.getDownloadURL();
})
.then(downloadURL => {
return downloadURL;
})
.catch(error => {
console.log(`An error occurred while uploading the file.\n\n${error}`);
});
}
The image uploads to firebase storage just fine. At the moment it just shows this when I try write the URL of the uploaded image to the database:
https://ibb.co/WHHHxBY
Here is the block of code where I create the user record:
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then(userCredentials => {
let imageUrl = '';
let db = firebase.database().ref('users/' + userCredentials.user.uid);
if (this.state.image) {
imageUrl = this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
}
db.set({
email: this.state.email,
imageUrl: imageUrl,
username: this.state.username
});
return userCredentials.user.updateProfile({
displayName: this.state.username
});
})
.catch(error => this.setState({ errorMessage: error.message }));
In your uploadImage function, you are chaining the promises but you don't return the chain. You should adapt it as follows:
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
return firebase.storage().ref().child(imageName).put(blob) // <-- Here return the chain
.then(snap => {
return snap.ref.getDownloadURL();
})
.then(downloadURL => {
return downloadURL;
})
.catch(error => {
console.log(`An error occurred while uploading the file.\n\n${error}`);
});
}
However, you could transform this code in async/await "style", as follows:
uploadImage = async (uri, imageName) => {
try {
const response = await fetch(uri);
const blob = await response.blob();
const snap = await firebase.storage().ref().child(imageName).put(blob);
const downloadURL = await snap.ref.getDownloadURL();
return downloadURL;
} catch (e) {
console.error(e);
throw e;
}
}
Then, since this uploadImage function is asynchronous you should adapt the way you call it. I suggest to modify the other part of your code as follows:
try {
const userCredentials = await firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password);
let imageUrl = '';
const db = firebase.database().ref('users/' + userCredentials.user.uid);
if (this.state.image) {
imageUrl = await this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
await db.set({
email: this.state.email,
imageUrl: imageUrl,
username: this.state.username
});
return userCredentials.user.updateProfile({
displayName: this.state.username
});
}
//You should probably manage the else case
} catch (e) {
this.setState({ errorMessage: e.message })
}
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();