Javascript intellisense and ctrl+click not working while using fixtures - javascript

I am performing playwright test automation using fixtures and have files as below. While running its working as expected so i know its not a playwright issue. My question is when i ctrl+click on loadAndLogin under test.step in TC_123.js with VS code, i am expecting it to navigate to the loadAndLogin method in LoadAndLogin.js. But this is not happening and how do i fix this?
// basePage.js
const base = require('#playwright/test');
const { LoadAndLogin } = require('../utilities/LoadAndLogin');
exports.test = base.test.extend({
loadAndLogin: async ({ page }, use) => {
await use(new LoadAndLogin(page));
},
});
exports.expect = base.expect;
// LoadAndLogin.js
const { test, expect } = require('#playwright/test');
const { LoginPage } = require('../pages/LoginPage');
exports.LoadAndLogin = class LoadAndLogin {
constructor(page) {
this.page = page;
this.loginPage = new LoginPage(page);
}
async loadAndLogin() {
// code to Login to the application
}
}
// TC_123.js
const { test } = require('../../fixtures/basePage');
test('TC_123', async function ({page, loadAndLogin}) {
await test.step('01_Load application and login', async function () {
await loadAndLogin.loadAndLogin();
});
});
I tried checking with playwright team in github and below was the response
https://github.com/microsoft/playwright/issues/20218
You need to either add // #ts-check and javascript type annotations and use type script to make the navigation work for fixtures. VSCode fails to infer all the types looking at the javascript code alone. Closing this issue as it is not a playwright defect. Also feel free to open a request for VS Code team.
i tried using //#ts-check and even created jsconfig.json but still i am not able to understand why it is not working

Related

Cypress won't open the specified URL

When I run the test, the specified URL does not open, even though it is entered correctly and set in BaseUrl in cypress.config.js:
> module.exports = defineConfig({ e2e: {
> "projectId": "fi4fhz",
> "viewportHeight": 1080,
> "viewportWidth": 1920,
> specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
> baseUrl: 'https://pha.mm.int:6001/',
>
> setupNodeEvents(on, config) {
>
> }, },
In every test file I have this:
beforeEach(() => {
cy.runInWeb();
});
and in commands.js I have:
Cypress.Commands.add("runInWeb", () => { cy.visit(Cypress.e2e().projectUrl) });
and in cypress.config.js I have:
"projectUrl": "https://pha.mm.int:6001/"
but it's not functioning. Where is the problem?
The baseUrl value https://pha.mm.int:6001/ shows up in the browser address bar because Cypress uses it to configure set up the runner.
But the <iframe> containing the web page under test isn't changed until you perform
the first cy.visit('/').
This is to allow you to execute code before the page load occurs, like cy.intercept() and cy.fixture().
Yes you visit() but not in the right way, if you store your url in env variable you get it out like this:
Cypress.Commands.add("runInWeb", () => { cy.visit(Cypress.env('projectUrl')) });
and you store the default like this:
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'
},
env: {
projectUrl: 'https://pha.mm.int:6001/',
},
})
or cypress.env.json
{
"projectUrl": "https://pha.mm.int:6001/"
}
Ensure that you have this beforeEach prior to running your it test blocks. You need to use cy.visit('/') before each of your tests.
beforeEach(() => {
//cy.session(id, setup); Use is if you want to stay logged into your application
cy.visit('/);
cy.wait(100);
});
If you're interested in using a session, visit the cypress docs here:
Based on the information in the question, I assume that you did not include a cy.visit() command. Without that command, Cypress does not know to open a webpage.

'X Is not a function' in CommonJS

I've got the following code I transformed from a Trypescript, ESM-syntax based file to a Javascript, CJS-syntax file.
const apiClientFactory = require("#vue-storefront/core");
function onCreate(settings) {
return {
config: settings,
client: {},
};
}
const getPrice = () => {
console.log("$55,98")
}
const { createApiClient } = apiClientFactory({
onCreate,
api: {
getPrice,
},
});
module.exports = {
createApiClient,
};
I can not seem to find if the error "apiClientFactory is not a function" originates from old ESM-based code. Or that the function isn't called properly. However, apiClientFactory is correctly imported (ESM syntax)
What are you trying to achieve with this?
Because the whole Vue Storefront project uses TypeScript, so I recommend you to use it and follow the procedures and code standards that we are using.
To find a good example on the API please check the code for the integrations of Magento or Vendure.

How to properly add a class inside Cypress code

I am learning Cypress along with JavaScript. I am running into a problem that I am not certain how to search it into documentation. The site I started testing has the typical wait issues so I encountered a very good solution here.
Now my test is looking in this way
/// <reference types="Cypress" />
let appHasStarted
function spyOnAddEventListener (win) {
// win = window object in our application
const addListener = win.EventTarget.prototype.addEventListener
win.EventTarget.prototype.addEventListener = function (name) {
if (name === 'change') {
// web app added an event listener to the input box -
// that means the web application has started
appHasStarted = true
// restore the original event listener
win.EventTarget.prototype.addEventListener = addListener
}
return addListener.apply(this, arguments)
}
}
function waitForAppStart() {
// keeps rechecking "appHasStarted" variable
return new Cypress.Promise((resolve, reject) => {
const isReady = () => {
if (appHasStarted) {
return resolve()
}
setTimeout(isReady, 0)
}
isReady()
})
}
describe('Main test suite', () => {
beforeEach(() => {
cy.visit('http://mercadolibre.com.ar',{
onBeforeLoad: spyOnAddEventListener
}).then({ timeout: 10000 }, waitForAppStart)
})
it('search first scanner', () => {
cy.contains('nav-search-input').type("scanner bluetooth para auto")
})
})
The problem with this is, I should replicate spyOnAddEventListener, waitForAppStart and variable appHasStarted at the beginning of every source file but I want to avoid this. How could properly extend this functions as a part of the internal source project without replicating in every test source? I have tried to make a simple source JavaScript file at the root of the project but when I import it, Cypress clients give an unrelated plug error like this one:
It looks like you've added the code to /cypress/plugins/index.js, but that is for task extensions (code that requires NodeJS access).
The two functions can be added to a file, ideally in the /cypress/support folder
wait-for-app-utils.js
let appHasStarted
function spyOnAddEventListener (win) {
...
}
function waitForAppStart() {
...
}
module.exports = {
spyOnAddEventListener,
waitForAppStart
}
test
import {spyOnAddEventListener, waitForAppStart} from '../support/wait-for-app-utils.js'
describe('Main test suite', () => {
beforeEach(() => {
cy.visit('http://mercadolibre.com.ar', {
onBeforeLoad: spyOnAddEventListener
}).then({ timeout: 10000 }, waitForAppStart)
})
Another approach is to wrap it all up (including the visit) into a custom command. Now there's no need to export and import, the command will be available globally.
/cypress/support/commands.js
let appHasStarted
function spyOnAddEventListener (win) {
...
}
function waitForAppStart() {
...
}
Cypress.Commands.add('visitAndWait', (url) =>
cy.visit(url, { onBeforeLoad: spyOnAddEventListener })
.then({ timeout: 10000 }, waitForAppStart)
)
test
describe('Main test suite', () => {
beforeEach(() => {
cy.visitAndWait('http://mercadolibre.com.ar')
})

Testing Cross Browser Extension With Jest, How To Mock Chrome Storage API?

After putting off testing for a while now due to Cypress not allowing visiting chrome:// urls, I decided to finally understand how to unit/integration test my extension - TabMerger. This comes after the many times that I had to manually test the ever growing functionality and in some cases forgot to check a thing or two. Having automated testing will certainly speed up the process and help me be more at peace when adding new functionality.
To do this, I chose Jest since my extension was made with React (CRA). I also used React Testing Library (#testing-library/react) to render all React components for testing.
As I recently made TabMerger open source, the full testing script can be found here
Here is the test case that I want to focus on for this question:
import React from "react";
import { render, fireEvent } from "#testing-library/react";
import * as TabFunc from "../src/Tab/Tab_functions";
import Tab from "../src/Tab/Tab";
var init_groups = {
"group-0": {
color: "#d6ffe0",
created: "11/12/2020 # 22:13:24",
tabs: [
{
title:
"Stack Overflow - Where Developers Learn, Share, & Build Careersaaaaaaaaaaaaaaaaaaaaaa",
url: "https://stackoverflow.com/",
},
{
title: "lichess.org • Free Online Chess",
url: "https://lichess.org/",
},
{
title: "Chess.com - Play Chess Online - Free Games",
url: "https://www.chess.com/",
},
],
title: "Chess",
},
"group-1": {
color: "#c7eeff",
created: "11/12/2020 # 22:15:11",
tabs: [
{
title: "Twitch",
url: "https://www.twitch.tv/",
},
{
title: "reddit: the front page of the internet",
url: "https://www.reddit.com/",
},
],
title: "Social",
},
};
describe("removeTab", () => {
it("correctly adjusts groups and counts when a tab is removed", () => {
var tabs = init_groups["group-0"].tabs;
const { container } = render(<Tab init_tabs={tabs} />);
expect(container.getElementsByClassName("draggable").length).toEqual(3);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
fireEvent.click(container.querySelector(".close-tab"));
expect(removeTabSpy).toHaveBeenCalledTimes(1);
expect(container.getElementsByClassName("draggable").length).toEqual(2); // fails (does not remove the tab for some reason)
});
});
I mocked the Chrome API according to my needs, but feel that something is missing. To mock the Chrome API I followed this post (along with many others, even for other test runners like Jasmine): testing chrome.storage.local.set with jest.
Even though the Chrome storage API is mocked, I think the issue lies in this function which gets called upon initial render. That is, I think the chrome.storage.local.get is not actually being executed, but am not sure why.
// ./src/Tab/Tab_functions.js
/**
* Sets the initial tabs based on Chrome's local storage upon initial render.
* If Chrome's local storage is empty, this is set to an empty array.
* #param {function} setTabs For re-rendering the group's tabs
* #param {string} id Used to get the correct group tabs
*/
export function setInitTabs(setTabs, id) {
chrome.storage.local.get("groups", (local) => {
var groups = local.groups;
setTabs((groups && groups[id] && groups[id].tabs) || []);
});
}
The reason I think the mocked Chrome storage API is not working properly is because when I manually set it in my tests, the number of tabs does not increase from 0. Which forced me to pass a prop (props.init_tabs) to my Tab component for testing purposes (https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35) - something I want to avoid if possible via setting local storage.
Can someone point me in the right direction? I would like to avoid using libraries like jest-chrome since they abstract too much and make it harder for me to understand what is going on in my tests.
I think I have a solution for this now, so I will share with others.
I made proper mocks for my chrome storage API to use localStorage:
// __mocks__/chromeMock.js
...
storage: {
local: {
...,
get: function (key, cb) {
const item = JSON.parse(localStorage.getItem(key));
cb({ [key]: item });
},
...,
set: function (obj, cb) {
const key = Object.keys(obj)[0];
localStorage.setItem(key, JSON.stringify(obj[key]));
cb();
},
},
...
},
...
Also, to simulate the tab settings on initial render, I have a beforeEach hook which sets my localStorage using the above mock:
// __tests__/Tab.spec.js
var init_ls_entry, init_tabs, mockSet;
beforeEach(() => {
chrome.storage.local.set({ groups: init_groups }, () => {});
init_ls_entry = JSON.parse(localStorage.getItem("groups"));
init_tabs = init_ls_entry["group-0"].tabs;
mockSet = jest.fn(); // mock for setState hooks
});
AND most importantly, when I render(<Tab/>), I noticed that I wasn't supplying the id prop which caused nothing to render (in terms of tabs from localStorage), so now I have this:
// __tests__/Tab.spec.js
describe("removeTab", () => {
it("correctly adjusts storage when a tab is removed", async () => {
const { container } = render(
<Tab id="group-0" setTabTotal={mockSet} setGroups={mockSet} />
);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
var chromeSetSpy = jest.spyOn(chrome.storage.local, "set");
fireEvent.click(container.querySelector(".close-tab"));
await waitFor(() => {
expect(chromeSetSpy).toHaveBeenCalled();
});
chrome.storage.local.get("groups", (local) => {
expect(init_tabs.length).toEqual(3);
expect(local.groups["group-0"].tabs.length).toEqual(2);
expect(removeTabSpy).toHaveBeenCalledTimes(1);
});
expect.assertions(4);
});
});
Which passes!!
Now on to drag and drop testing 😊

Jest: Trying to mock nested functions and redirects in jest, but no avail

We are using a javascript framework(Not react) to render the ui.
main.js
function logout(){
someObj.lock($('#container'), 'Logging out', true);
document.location = app.context + `/${appName}/signout.action?name=${appName}`;
}
function action(event){
switch(event.target){
case 'user:logout':
logout();
break;
case 'user:application':
document.location = app.context + "/";
break;
}
}
module.exports = {
action: action,
logout: logout
}
main.js along with another js file renders a navbar and a dropdown. My intention is to check whether title, dropdown in the navbar is rendered. Also I
am testing whether the browser redirect takes place in the right way.
action method takes an event object and based on its type, either performs signout('user:logout') or redirects to application page('user:application').
tests/main.js
import main from '../main';
describe("some title", () => {
it("some behavior", () => {
let event = {
target: 'user:logout'
}
let app = {
context: ''
}
let appName = 'Some app';
main.logout = jest.fn();
someObj = jest.fn();
someObj.lock = jest.fn();
document.location.assign = jest.fn();
main.action(event);
expect(main.logout).toHaveBeenCalled();
expect(document.location.assign).toBeCalledWith(app.context + `/${appName}/signout.action?name=${appName}`);
})
});
In the test file, I am trying to mock logout function. However it is executing someObj.lock function. someObj is not availabe to tests/main.js
and I am mocking it as well. I'm not sure whether I have to use spyOn instead. I'm using document.location.assign to test for browser redirects.
This test is not working and the terminal is displaying TypeError: Could not parse "/application name/signout.action?name=application name" as URL.
I have spent an entire day testing this feature but to no avail. I need some advice on the best way to test this feature.
Links explored: Intercept navigation change with jest.js (or how to override and restore location.href)
jest documentation

Categories

Resources