Playwright - How to access TestInfo outside of test function? - node.js

In Playwright, is there any way to access current TestInfo outside of a test function?
I have some tests that need access to the current testInfo.snapshotDir. However, the 'e_compareImages' function that needs the testInfo is nested several layers deep. It is not practical to pass the testInfo from the test function all the way to the function that needs that info.
I know e_compareImages() already has some access to the current testInfo because the function calls toMatchSnapshot() (from Jest's expect library), and the function pulls the correct file from the current testInfo.snapshotDir
Is there any way for functions in the actionHelper file to access current testInfo outside of an expect assertion?
I've posted what I believe are the relevant portions of the files below.
// LoginPage.test.js File
const { test } = require('#playwright/test');
test("Verify logo on Login page", async () => {
await loginPage.verifyLogo();
});
// actionHelper.js File
const { expect } = require('#playwright/test');
async e_compareImages(selector, expectedImageFileName) {
let locator= await this.page.locator(selector);
const actualImage = await locator.screenshot({scale: 'device'});
await expect(actualImage).toMatchSnapshot(expectedImageFileName);
}
I've tried importing Playwright's globals.js library and using its currentTestInfo(), but I have been unable to get it to work.
Is there another library or a way to extend an existing library that would do what I need?
Please let me know if you need any additional information.

You can access TestInfo object outside of a test function using the test method's second argument:
async e_compareImages(testInfo, selector, expectedImageFileName) {
// ...
const actualImage = await locator.screenshot({scale: 'device'});
await expect(actualImage).toMatchSnapshot(testInfo, expectedImageFileName);
}
Now, when you call e_compareImage function you can pass the TestInfo object as an argument:
test("Verify logo on Login page", async (testInfo) => {
await loginPage.verifyLogo(testInfo);
});

I found what I was looking for. You can use the below code to require the globals library
var _globals = require("../node_modules/#playwright/test/lib/globals");
Then you can use this function to get the current testInfo object directly (without having to pass the object through however many intermediary functions)
// Get the current test's testInfo object
async getCurrentTestInfo() {
const curTestInfo = await (0, _globals.currentTestInfo)();
return await curTestInfo
}

Related

await Error at Top Level Module in Chrome Extension Service_worker

I have a sequence of code that is at the top level of my script:
await tmrs=chrome.storage.local.get("tmrs");
console.log(tmrs);
await data=chrome.storage.local.get("Auto_Select_051969");
console.log(data);
if (data==null) {
store_data();
await data=chrome.storage.local.get("Auto_Select_051969");
}
However I get the error:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
This is in a service_worker script. Is there some kind of restriction? How do I fix this? TIA.
I found this: Getting this error "await is only valid in async functions and the top level bodies of modules" in chrome extension with async await but I'm not sure this applies or if the anonymous function is really the solution.
Updated Code:
(async () => {
let tmrs=await chrome.storage.local.get("tmrs");
})();
console.log(tmrs);
Wrap all of your service worker code in an async IIFE, including the definition of store_data().
(async () => {
function store_data() {
console.log("storing data");
}
tmrs = await chrome.storage.local.get("tmrs");
console.log(tmrs);
data = await chrome.storage.local.get("Auto_Select_051969");
console.log(data);
if (data == null) {
store_data();
data = await chrome.storage.local.get("Auto_Select_051969");
}
})();
Explanation:
https://usefulangle.com/post/248/javascript-async-anonymous-function-iife
Immediately-invoked Function Expression (IIFE), is a technique to
execute a Javascript function as soon as they are created. It is good
way of declaring variables and executing code without polluting the
global namespace. These are also called anonymous functions as they
have no defined names.
Very often it is required to use the await operator immediately (for
example page load). The await can however be used only inside an async
function. But instead of defining a new async function and calling it,
it is better to use the IIFE pattern to create an async function that
will be immediately called.

client.on is not a function (not discord.js related)

I'm creating my own package that needs to do some emitting from the package to the main file.
This simplified module gets what I'm trying to achieve:
const fetch = require("node-fetch")
module.exports.client = async function(username,password,version) {
// Do login code here
this.emit("login", loginData)
}
Then my test code runs that with
var client = new require("../index").client("username","password","0.774")
client.on("login",function(data) {
console.log(data)
})
With every time, the program returning
client.on("login",function(data) {
^
TypeError: client.on is not a function
Has anyone had a similar issue? This is my first time making a module in this style, so please let me know.
Thank you!
Because your function client() exported from index.js is declared with the async keyword, it will return a Promise rather than an actual value passed to the return statement. Assuming the value you return from the function client() is an object with an on() function, then in order to access this function you either need to await the result of client() or you need to use the .then() function on promise. Example:
var clientPromise = new require("../index").client("username","password","0.774")
clientPromise.then(client => {
client.on("login",function(data) {
console.log(data)
})
});
While you could also use await on the result of the client() function, this is not allowed in all contexts, and so you would have to wrap the code that does this in another async function.

How to mock a function and expect it to be called?

I am new to react-testing-library and I have been trying to test one function for a long time.
for example, I want to check if when a button is clicked a given function is called and it's throwing errors. so any help would be highly appreciated and if possible share with me any helpful resources.
signin.js
export default class SignIn extends Component {
constructor(props) {
super(props);
this.state = {
};
}
handleClose = (event, reason) => { };
validate = () => { };
change = (e) => { };
onSubmit = (e) => { };
render() {
return (<div>...</div>);
}
}
Full: https://github.com/blaise82/react-testing-library-try/blob/master/src/views/SignIn.js
this is my test
it('should submit form', async () => {
const { getByLabelText, getByText, container, debug } = render(<SignIn />);
const change = jest.fn();
const onSubmit = jest.fn();
const email = getByLabelText('email');
const password = getByLabelText('password');
const submit = getByLabelText('submit');
userEvent.type(email, 'octopusbn#gmail.com');
expect(email.value).toBe('octopusbn#gmail.com');
expect(password.value).toBe('');
expect(change).toHaveBeenCalled();
console.log(password)
await userEvent.click(submit);
expect(onSubmit).toHaveBeenCalled();
});
Full: https://github.com/blaise82/react-testing-library-try/blob/master/src/test/signin.test.js
results
> Expected number of calls: >= 1
> Received number of calls: 0
please let know what I am doing wrong.
Full code on GitHub: https://github.com/blaise82/react-testing-library-try
You can test a function by mocking all that is coming from outside of the component (aka dependencies) like - a prop callback, an external library api etc.
Before starting, let's go through what all functions are in the component.
Going through the component, I can list them as below:
Event handlers on elements [like handleClose, onSubmit, change in the component]
Functions internal to the component which do not interact with the state/functions outside the component [validate]
prop functions/library apis being called [axios.post]
Let's discuss them one by one --
Event handlers &
Functions internal to component not interacting with state/functions outside of the component
==> Event handlers that are attached to elements can safely be expected to get called. You don't need to test them if they are called. Rather, what you should test is the after-effect of them being called. Also the functions like validate
Let's take example of the change function that you are trying to test. This function after being called sets the state and the state gets reflected into the form elements. We can assert values of the form elements with a helper like this.
prop functions/library apis being called [axios.post]
==> These functions can be mocked and tested for the number of calls/parameters they are called with.
https://jestjs.io/docs/en/mock-functions.html#mocking-modules
In addition to the snippet of mocking jest as given in the link above, in your case -
axios.post.toHaveBeenCalledWith(expectedParms);
Also you can make it return results/errors you want and test respective component behaviour.
Hope you find this helpful. Cheers!
I think this is because you are not actually passing in your mocked functions to the component. You're just instantiating two constants that happen to have the name of the functions you're trying to watch, but are not actually being used anywhere in your component.
It sounds like you want to spy on your component's internal functions to see that they've been called.
Here's something of an example (not tested) based on a post (linked below) that might help you.
describe('spying on "onSubmit" method', () => {
it('should call onSubmit when the button is clicked', () => {
const wrapper = shallow(<SignIn />);
const instance = wrapper.instance();
jest.spyOn(instance, 'onSubmit');
wrapper.find('button').simulate('click');
expect(instance.onSubmit).toHaveBeenCalled();
});
});
Post: https://bambielli.com/til/2018-03-04-directly-test-react-component-methods/#spying-on-incrementcounter

ES6 class jest mocking

I have an ES6 class which I need to mock it's methods. Following the documentation i made a manual mock of this, and got the constructor to both be called and asserted.
My function that consumes this class is just a basic function that runs one of the class methods.
test.js
const mockConnect = jest.fn();
const mockAccess = jest.fn();
jest.mock('../../src/connection');
const connection = require('../../src/connection').default;
connection.mockImplementation(() => {
return {
connect: mockConnect,
access: mockAccess.mockReturnValue(true),
};
});
caller_function();
expect(connection).toHaveBeenCalled(); // works properly as the constructor is called
expect(connection).toHaveBeenCalledWith('something'); // works
expect(mockAccess).toHaveBeenCalled(); // says it was not called when it should have
caller_function.js
import connection from 'connection';
const conn = new connection('something');
export function caller_function() {
conn.access(); // returns undefined when mock says it should return true
}
This is happening because you're using mockImplementation() instead of a manual mock or the factory parameter to jest.mock(), and your mocked object is being created during the module loading process, since the constructor call is not inside of any function. What's happening is:
The call to jest.mock('../../src/connection') runs and sets connection to be an automatic mock.
The conn object is created using the automatic mock. Therefore its access method returns undefined.
The call to mockImplementation() happens, changing the connection mock. However, since the conn object has already been created, it doesn't get the custom implementation.
Moving the constructor call into caller_function is one way to fix it:
export function caller_function() {
const conn = new connection('something');
conn.access();
}
You could also use the factory parameter to jest.mock(), specifying the implementation there, instead of calling mockImplementation(). That way you won't have to change your implementation code:
const mockConnect = jest.fn();
const mockAccess = jest.fn();
import connection from '../../src/connection';
jest.mock('./so-import', () => {
return jest.fn().mockImplementation(() => {
return {
connect: mockConnect,
access: mockAccess.mockReturnValue(true)
};
});
});
...
BTW the convention for ES6 class names is to begin with an uppercase letter. I was temporarily confused by the lowercase name connection.
Did you try doing connection.mockClear(); before you write a mockImplementation for the methods?
Also please refer to this https://jestjs.io/docs/en/es6-class-mocks

nodejs - Export queries result to other files

I'm doing some query to retrieve some data from a database, and trying to export said data to be used in my nodejs aplication. But everything I've tried so far, does not work.
r.js
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data;
});
return test;
}
module.exports = { user: site().then(data=>{return data})}
but I always get Promise pending. Even when I do the imports:
import users = require("./r")
users.then(data=>{return data})
and still doesnt work. How can I fix this?
Thank you,
For starters, there's no reason to resolve a promise and immediately return the same object resolved in its then block. Just omit the "then" if there is nothing else you need to do.
So this:
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data; <--- this isn't necessary. Only adds noise unless there is something else you need to do. It's similar to "catching" and immediately "rethrowing" an error... just pointless
});
return test;
}
Can be this:
async function site() {
var test = await db
.select("*")
.from("site_credentials");
return test;
}
Secondly, I'm not really sure why you're trying to resolve it in the export. Just export the function.
module.exports = site;
Then when you require it elsewhere in your app, call it and resolve it there:
const users = require("./r")
users.then(data=>{
// do something with your data here...
})
Note that in your first example, you are exporting an object, containing a "users" property which is the function. If you do it that way, you would need to invoke it like so:
const users = require("./r")
users.users().then(data=>{
// do something with your data here...
})
You can see that users.users clearly doesn't make sense. So, export properly to avoid that. Export only the function itself, not nested inside some other object.
But, if you look closely, you'll notice another thing I did wrong. I'm exporting a "site" function, yet requiring it in as a "users" function. Naming conventions matter. If this function is called "site" here, you ought to require (or import depending on your module loader...) it in as "site"... thus:
const site = require('./r');
Otherwise you just confuse the crud out of a fellow developer.

Resources