I'm trying to use Playwright to automate authentication in my web application.
When I did the authentication test in a typical .spec.ts file, it succeeded:
test('bnblnlnnl', async ({ page }) => {
await page.goto('/');
await page.getByTestId('auth-github-auth-button').click();
await page.getByLabel('Username or email address').fill('automations#blabla');
await page.getByLabel('Password').fill('sdfgsdgsdfgfgf');
await page.getByRole('button', { name: 'Sign in' }).click();
const authorizeElement = page.getByRole('button', { name: 'Authorize blabla' });
const shouldAuthorize = await authorizeElement.isVisible();
if (shouldAuthorize) {
await authorizeElement.click();
}
const navElemnt = page.getByTestId('nav');
await expect(navElemnt).toBeVisible();
await expect(page).toHaveURL('/');
});
So this test successfully completes. Then, according to this documentation: https://playwright.dev/docs/auth
I can authenticate already in the global setup script, instead of authenticating before each test block. To do so, I have this script for my global setup file:
import { chromium } from '#playwright/test';
const globalSetup = async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://localhost:8080/');
await page.getByTestId('auth-github-auth-button').click();
await page.getByLabel('Username or email address').fill('gfsdagdf');
await page.getByLabel('Password').fill('sadfsdfsdfs');
await page.getByRole('button', { name: 'Sign in' }).click();
const authorizeElement = page.getByRole('button', { name: 'Authorize dfssd' });
const shouldAuthorize = await authorizeElement.isVisible();
if (shouldAuthorize) {
await authorizeElement.click();
}
await page.context().storageState({ path: 'storageState.json' });
await browser.close();
};
export default globalSetup;
But when I run playwright test I get a timeout from this statement: await page.getByTestId('auth-github-auth-button').click();.
The error message:
{
"name": "TimeoutError"
}
So I checked, during test process- I browsed to http://localhost:8080 and I saw my web app is running, and the element with id auth-github-auth-button does present, including its data-test-id attribute. So why playwright fails to locate it?
This is my playwright.config.ts file:
import { defineConfig } from '#playwright/test';
const configuration = defineConfig({
testDir: './tests',
testIgnore: 'scripts',
globalSetup: './tests/scripts/global-setup.ts',
globalTeardown: './tests/scripts/global-teardown.ts',
reporter: [['html', { open: 'never' }]],
use: {
testIdAttribute: 'data-test-id',
baseURL: 'http://localhost:8080',
storageState: 'storageState.json',
},
});
export default configuration;
As you noted in your answer, the issue was that the config doesn’t affect the global setup, and so Playwright tried to use the default data-testid attribute instead of your custom attribute.
While one solution would be to switch to using data-testid attributes instead to match the default, I wanted to offer up an alternative to keep your custom attribute. According to the Playwright docs on setting a custom test id attribute, “you can configure it in your test config or by calling selectors.setTestIdAttribute().” While the config option won’t automatically work for the global setup as you mentioned in your answer, you should be able to use it as passed into your setup along with selectors.setTestIdAttribute() to use your custom attribute as expected.
So this suggested change to the top of your setup file should theoretically make it work as you expected:
import { chromium, selectors, FullConfig } from '#playwright/test';
const globalSetup = async (config: FullConfig) => {
const { testIdAttribute } = config.projects[0].use;
selectors.setTestIdAttribute(testIdAttribute);
const browser = await chromium.launch();
See the docs about global setup for their example of using the config object inside the setup to reuse values. Theirs uses baseURL and storageState, which you may find value in as well.
Hope that helps!
The issue is that I'm using data-test-id but in global setup script only data-testid will work as it's not configurable. Changing all my attributes to data-testid solved it
I'm tried to test a React Native library using Jest, but it show error, can someone help me to solve my problem.
Here my Fucntion code :
export const createChannel = (): void => {
PushNotification.createChannel({
channelId: 'test-channel',
channelName: 'Test Channel',
vibrate: true,
});
};
I'm using react-native-push-notification library to this function
and here's my testing code :
import PushNotification from 'react-native-push-notification';
import {createChannel} from '../src/functions/PomodoroFunction';
jest.mock('react-native-push-notification', () => 'PushNotification.createChannel');
describe('Create Channel unit test', () => {
it('Should be called',()=>{
const mockFN = createChannel()
expect(mockFN).toHaveBeenCalled();
})
});
error shown : TypeError: _reactNativePushNotification.default.createChannel is not a function
Can someone help me solve this, thankyou so much!
Try
import * as PushNotification from 'react-native-push-notification';
...
const create = jest.fn();
const rnpn = jest.spyOn(PushNotification, 'default').mockImplementation(() => {
return { createChannel: create } as any;
});
...
And then use create in your test. I'm not sure what you're testing in the test above, but this is a good way to mock functions you don't want to run on your test.
I'm in the process of setting a graphql endpoint with servlerless/ lambda and am receiving an error when trying to connect to the graphql playground that comes with graphql-yoga. When I go to my route that has the playground (/playground) it launches the playground interface however it just says:
Server cannot be reached
In the top right of the playground. It's worth noting i'm using the makeRemoteExecutableSchema utility to proxy to another graphql endpoint (which is my CMS called Prismic). I don't believe this is the issue as I have successfully connected to it with the playground when testing on a normal express server.
Here is the code in my handler.js
'use strict';
const { makeRemoteExecutableSchema } = require('graphql-tools');
const { PrismicLink } = require("apollo-link-prismic");
const { introspectSchema } = require('graphql-tools');
const { ACCESS_TOKEN, CMS_URL } = process.env;
const { GraphQLServerLambda } = require('graphql-yoga')
const lambda = async () => {
const link = PrismicLink({
uri: CMS_URL,
accessToken: ACCESS_TOKEN
});
const schema = await introspectSchema(link);
const executableSchema = makeRemoteExecutableSchema({
schema,
link,
});
return new GraphQLServerLambda({
schema: executableSchema,
context: req => ({ ...req })
});
}
exports.playground = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const graphQl = await lambda();
return graphQl.playgroundHandler(event, context, callback);
};
I have followed this guide for getting it running up till here and am fairly sure i've followed similar steps for what applies to what i'm trying to do but can't seem to figure out where i've gone wrong.
Thanks,
Could you take a look at what version of the graphql-yoga package you are using?
I had a similar problem using the Apollo server in combination with Kentico Cloud Headless CMS and I found this issue:
https://github.com/prisma/graphql-yoga/issues/267
We're looking to develop an ElectronJS app for particular website automation at our desk job, which includes common tasks like login, form filling, report downloading etc.
We've tried basic tutorial of ElectronJS, Spectron, NightmareJS, Puppeteer etc and all of them work fine separately, but very less documentation (although open github issues) are available on integration of each other.
We want to achieve following:
Login state (session) should not be deleted on ElectronJS app closing and should be available on restart of app.
Few menu buttons which initiates some automation tasks like download, form fill etc on existing browserWindow
We don't need headless automation, where some magic happens behind the scene. We need menu/button click based actions/tasks on current page only.
NightmareJS, Puppeteer etc all seems to start their own instances of web pages (since because they were built for testing of standalone apps) but what we need is automation of existing BrowserWindows.
Is puppeteer or nightmarejs correct tools for such goals? If yes, any documentation?
Or else, should we inject our own native JS events like mouseclick etc events in console to perform action?
You can use puppeteer-core. core version by default does not download Chromium, which you do not need if you want to control an Electron app.
In the test you then call launch method, where you define electron as the executable file instead of Chromium, like in following snippet:
const electron = require("electron");
const puppeteer = require("puppeteer-core");
const delay = ms =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
(async () => {
try {
const app = await puppeteer.launch({
executablePath: electron,
args: ["."],
headless: false,
});
const pages = await app.pages();
const [page] = pages;
await page.setViewport({ width: 1200, height: 700 });
await delay(5000);
const image = await page.screenshot();
console.log(image);
await page.close();
await delay(2000);
await app.close();
} catch (error) {
console.error(error);
}
})();
Update for electron 5.x.y and up (currently up to 7.x.y, I did not test it on 8.x.y beta yet), where puppeteer.connect is used instead of launch method:
// const assert = require("assert");
const electron = require("electron");
const kill = require("tree-kill");
const puppeteer = require("puppeteer-core");
const { spawn } = require("child_process");
let pid;
const run = async () => {
const port = 9200; // Debugging port
const startTime = Date.now();
const timeout = 20000; // Timeout in miliseconds
let app;
// Start Electron with custom debugging port
pid = spawn(electron, [".", `--remote-debugging-port=${port}`], {
shell: true
}).pid;
// Wait for Puppeteer to connect
while (!app) {
try {
app = await puppeteer.connect({
browserURL: `http://localhost:${port}`,
defaultViewport: { width: 1000, height: 600 } // Optional I think
});
} catch (error) {
if (Date.now() > startTime + timeout) {
throw error;
}
}
}
// Do something, e.g.:
// const [page] = await app.pages();
// await page.waitForSelector("#someid")//
// const text = await page.$eval("#someid", element => element.innerText);
// assert(text === "Your expected text");
// await page.close();
};
run()
.then(() => {
// Do something
})
.catch(error => {
// Do something
kill(pid, () => {
process.exit(1);
});
});
Getting the pid and using kill is optional. For running the script on some CI platform it does not matter, but for local environment you would have to close the electron app manually after each failed try.
Simple demo repo:
https://github.com/peterdanis/electron-puppeteer-demo
Automation Script in Java using Selenium and ChromeDriver
package setUp;
import helper.Constants;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
public class Test {
public static void main(String[] args) {
System.setProperty(Constants.WebDriverType, Constants.WebDriverPath + Constants.WindowsDriver);
ChromeOptions opt = new ChromeOptions();
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("chromeOptions", opt);
capabilities.setBrowserName("chrome");
capabilities.setVersion("73.0.3683.121");
ChromeOptions options = new ChromeOptions();
options.merge(capabilities);
options.setBinary("C:\\\\Program Files\\\\Audio\\\\Audio-Configuration\\\\Audio-Configuration.exe");
options.setCapability("chromeOptions", options);
ChromeDriver driver = new ChromeDriver(options);
try {
Thread.sleep(5000);
WebElement webElement = driver.findElement(By.xpath(
"/html/body/app-root/mat-drawer-container/mat-drawer/div/app-bottom-side-nav/div/app-settings-nav/div/div/a/div"));
webElement.click();
} catch (Exception e) {
System.out.println("Exception trace");
System.out.println(e);
}
}
}
Automation Script in JavaScript using Spectron (built on top-of ChromeDriver and WebDriverIO).
const Application = require("spectron").Application;
const path =
"C:/Program Files/Audio/Audio-Configuration/Audio-Configuration.exe";
const myApp = new Application({
path: path,
chromeDriverArgs: ["--disable-extensions"],
env: {
SPECTRON: true,
ELECTRON_ENABLE_LOGGING: true,
ELECTRON_ENABLE_STACK_DUMPING: true
}
});
const windowClick = async app => {
await app.start();
try {
// Identifying by class name
await app.client.click(".ic-setting");
// Identifying by Id
// await app.client.click("#left-btn");
} catch (error) {
// Log any failures
console.error("Test failed", error.message);
}
// Stop the application
await app.stop();
};
windowClick(myApp);
Spectron is the best match for electron build applications.
You will have access to all electron API.we can start and stop your app by spectron only.
We can run both packaged app or with out even packaging.
https://electronjs.org/spectron
You can use Spectron but if you want to look at documentation, Spectron is using webdriverio which has good documentation.
I recommend you to use Spectron because I tried to automate my tests with java-selenium but it fails some of case. If you want to use selenium, write below code to set capabilities to setup electron app to chromedriver.
ChromeOptions options = new ChromeOptions();
options.setBinary(binaryPath);
options.addArguments("--app=" + argPath);
options.setCapability("chromeOptions", options);
driver = new ChromeDriver(options);
Hope this will help to you.
If integrating with electron nightmare is a very good library to achieve this even it will be ready to distribute with it, here is the following useful documentation for the same resource1
and
I have this error when I compile my code in node.js, how can I fix it?
RefernceError: fetch is not defined
This is the function I am doing, it is responsible for recovering information from a specific movie database.
function getMovieTitles(substr){
pageNumber=1;
let url = 'https://jsonmock.hackerrank.com/api/movies/search/?Title=' + substr + "&page=" + pageNumber;
fetch(url).then((resp) => resp.json()).then(function(data) {
let movies = data.data;
let totPages = data.total_pages;
let sortArray = [];
for(let i=0; i<movies.length;i++){
sortArray.push(data.data[i].Title);
}
for(let i=2; i<=totPages; i++){
let newPage = i;
let url1 = 'https://jsonmock.hackerrank.com/api/movies/search/?Title=' + substr + "&page=" + newPage;
fetch(url1).then(function(response) {
var contentType = response.headers.get("content-type");
if(contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then(function(json) {
//console.log(json); //uncomment this console.log to see the JSON data.
for(let i=0; i<json.data.length;i++){
sortArray.push(json.data[i].Title);
}
if(i==totPages)console.log(sortArray.sort());
});
} else {
console.log("Oops, we haven't got JSON!");
}
});
}
})
.catch(function(error) {
console.log(error);
});
}
If you're using a version of Node prior to 18, the fetch API is not implemented out-of-the-box and you'll need to use an external module for that, like node-fetch.
Install it in your Node application like this
npm install node-fetch
then put the line below at the top of the files where you are using the fetch API:
import fetch from "node-fetch";
This is a quick dirty fix, please try to eliminate this usage in production code.
If fetch has to be accessible with a global scope
import fetch from 'node-fetch'
globalThis.fetch = fetch
You can use cross-fetch from #lquixada
Platform agnostic: browsers, node or react native
Install
npm install --save cross-fetch
Usage
With promises:
import fetch from 'cross-fetch';
// Or just: import 'cross-fetch/polyfill';
fetch('//api.github.com/users/lquixada')
.then(res => {
if (res.status >= 400) {
throw new Error("Bad response from server");
}
return res.json();
})
.then(user => {
console.log(user);
})
.catch(err => {
console.error(err);
});
With async/await:
import fetch from 'cross-fetch';
// Or just: import 'cross-fetch/polyfill';
(async () => {
try {
const res = await fetch('//api.github.com/users/lquixada');
if (res.status >= 400) {
throw new Error("Bad response from server");
}
const user = await res.json();
console.log(user);
} catch (err) {
console.error(err);
}
})();
If you want to avoid npm install and not running in browser, you can also use nodejs https module;
const https = require('https')
const url = "https://jsonmock.hackerrank.com/api/movies";
https.get(url, res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
data = JSON.parse(data);
console.log(data);
})
}).on('error', err => {
console.log(err.message);
})
fetch came to Node v17 under experimental flag --experimental-fetch
It will be available in Node v18 without the flag.
https://github.com/nodejs/node/pull/41749#issue-1118239565
You no longer need any additional package to be installed
EDITED - New Solution
To use the latest version (3.0.0) you must do the import like this:
const fetch = (url) => import('node-fetch').then(({default: fetch}) => fetch(url));
Old Anwser:
This may not be the best solution, but if you install this version :
npm install node-fetch#1.7.3
you can now use the line below without error's.
const fetch = require("node-fetch");
Node.js hasn't implemented the fetch() method, but you can use one of the external modules of this fantastic execution environment for JavaScript.
In one of the other answers, "node-fetch" is cited and that's a good choice.
In your project folder (the directory where you have the .js scripts) install that module with the command:
npm i node-fetch --save
Then use it as a constant in the script you want to execute with Node.js, something like this:
const fetch = require("node-fetch");
You should add this import in your file:
import * as fetch from 'node-fetch';
And then, run this code to add the node-fetch:
$ yarn add node-fetch
If you're working with typescript, then install node-fetch types:
$ yarn add #types/node-fetch
Best one is Axios library for fetching.
use npm i --save axios for installng and use it like fetch, just write axios instead of fetch and then get response in then().
You have to use the isomorphic-fetch module to your Node project because Node does not contain Fetch API yet. For fixing this problem run below command:
npm install --save isomorphic-fetch es6-promise
After installation use below code in your project:
import "isomorphic-fetch"
For those also using typescript on node-js and are getting a ReferenceError: fetch is not defined error
npm install these packages:
"amazon-cognito-identity-js": "3.0.11"
"node-fetch": "^2.3.0"
Then include:
import Global = NodeJS.Global;
export interface GlobalWithCognitoFix extends Global {
fetch: any
}
declare const global: GlobalWithCognitoFix;
global.fetch = require('node-fetch');
It seems fetch support URL scheme with "http" or "https" for CORS request.
Install node fetch library npm install node-fetch, read the file and parse to json.
const fs = require('fs')
const readJson = filename => {
return new Promise((resolve, reject) => {
if (filename.toLowerCase().endsWith(".json")) {
fs.readFile(filename, (err, data) => {
if (err) {
reject(err)
return
}
resolve(JSON.parse(data))
})
}
else {
reject(new Error("Invalid filetype, <*.json> required."))
return
}
})
}
// usage
const filename = "../data.json"
readJson(filename).then(data => console.log(data)).catch(err => console.log(err.message))
In node.js you can use : node-fetch package
npm i node-fetch
then :
import fetch from 'node-fetch';
here is a full sample in (nodejs) :
import fetch from "node-fetch";
const fetchData = async () => {
const res = await fetch("https://restcountries.eu/rest/v2/alpha/col"); // fetch() returns a promise, so we need to wait for it
const country = await res.json(); // res is now only an HTTP response, so we need to call res.json()
console.log(country); // Columbia's data will be logged to the dev console
};
fetchData();
In HackerRank, some libraries are installed by default and some are not.
Because it is running Node.js, the fetch API is not installed by default.
The best thing for you to do is to check whether the libraries are or not installed.
on the top of the exercise, there is the following:
const https = require('https');
Please try to add this to the top as well:
const axios = require('axios');
and then run the code.
If there is a compilation error, then it's not available, otherwise you can use axios, which is a good alternative to fetch
To use it with then, you can:
function getMovieTitles(substr){
axios.get(url)
.then(function(response){
console.log(response.data);
})
}
or taking advantage of the async/await
async function getMovieTitles(substr){
let response = await axios.get(url)
console.log(response.data);
}
This is the related github issue
This bug is related to the 2.0.0 version, you can solve it by simply upgrading to version 2.1.0.
You can run
npm i graphql-request#2.1.0-next.1
The following works for me in Node.js 12.x:
npm i node-fetch;
to initialize the Dropbox instance:
var Dropbox = require("dropbox").Dropbox;
var dbx = new Dropbox({
accessToken: <your access token>,
fetch: require("node-fetch")
});
to e.g. upload a content (an asynchronous method used in this case):
await dbx.filesUpload({
contents: <your content>,
path: <file path>
});
This worked for me:
const nodeFetch = require('node-fetch') as typeof fetch;
For me these are looking more simple.
npm install node-fetch
import fetch from "node-fetch";
There are actually a lot of different libraries for making fetch available in the browser.
The main ones I'm aware of are:
node-fetch
cross-fetch
whatwg-fetch
isomorphic-fetch
I currently use node-fetch, and it has worked fine, but I don't really know which one is "the best". (though the openbase.com pages I linked to provide some metadata on usage [eg. Github stars, npm downloads], which can help)
npm i node-fetch
Once installed, in your JavaScript file:
import fetch from "node-fetch";
Lastly make this change package.json file:
"type": "module"
Just make your app.js file Extension as app.mjs and the problem will be solved!!!:)
Solution without installations
Method 1
import { PLATFORM_ID } from '#angular/core';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
constructor(#Inject(PLATFORM_ID) private platformId: Object) {
// constructor code
}
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Client only code. Any javascript base functions
}
if (isPlatformServer(this.platformId)) {
// Server only code. Any javascript base functions
}
}
Method 2
import { PLATFORM_ID} from '#angular/core';
import { isPlatformBrowser } from '#angular/common';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavigationComponent implements OnInit {
private isBrowser: boolean = false;
constructor(
#Inject(PLATFORM_ID) private platformId: Object
) {
this.isBrowser = isPlatformBrowser(platformId);
}
ngOnInit(): void {
if (this.isBrowser) {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((json) => console.log(json));
}
}
}
DEMO - JSFIDDLE - Open console to view the fetch api service working
This answer does not directly answer this question. Instead it suggests for an alternative.
Why? Because the using 'node-fetch' is getting complicated since you cannot import the updated versions using const fetch = require('node-fetch') . You will have to do more things to just make it work.
Try using axios package:
Simple installation npm i axios
code for fetching goes like
const response = await axios.get(url).then(res => res.data)
Might sound silly but I simply called npm i node-fetch --save in the wrong project. Make sure you are in the correct directory.
If need install:
npm install --save global-fetch
then
var fetch = require("node-fetch");