Upload a file with puppeteer in Jest - chromium

I am using Jest and have puppeteer set up as in this repository, which is linked to from the Jest documentation.
I am trying to write some automated smoke tests on a WordPress website using puppeteer. One of the tests attempts to upload an image to the WordPress Media Library.
This is the test:
it('Create test media', async () => {
// go to Media > Add New
await page.goto(`${env.WP_HOME}/wp/wp-admin/media-new.php`)
const display = await page.evaluate(() => {
const el = document.querySelector('#html-upload-ui')
return window.getComputedStyle(el).display
})
if (display !== 'block') {
// ensure we use "built-in uploader" as it has `input[type=file]`
await page.click('.upload-flash-bypass > a')
}
const input = await page.$('#async-upload')
await input.uploadFile(testMedia.path)
})
The file input field's value is populated as expected (I know this because if I save out a screenshot after the call to uploadFile it shows the path of the file in the input), and the form is submitted, however when I go to view the media library there are no items.
I have tried the following amendments to the uploadFile part of the test, to no avail:
// 1. attempt to give time for the upload to complete
await input.uploadFile(testMedia.path)
await page.waitFor(5000)
// 2. attempt to wait until there is no network activity
await Promise.all([
input.uploadFile(testMedia.path),
page.waitForNavigation({waitUntil: 'networkidle0'})
])
// 3. attempt to submit form manually (programmatic)
input.uploadFile(testMedia.path)
page.evaluate(() => document.querySelector('#file-form').submit())
await page.waitFor(5000) // or w/ `waitForNavigation()`
// 4. attempt to submit form manually (by interaction)
input.uploadFile(testMedia.path)
page.click('#html-upload')
await page.waitFor(5000) // or w/ `waitForNavigation()`

The problem is that file uploading doesn't work when connecting to a Browser instance via WebSocket as in jest-puppeteer-example. (GitHub issue here: #2120.)
So instead of doing that just use puppeteer.launch() directly when setting up your test suite (instead of via the custom "Jest Node environment"):
let browser
, page
beforeAll(async () => {
// get a page via puppeteer
browser = await puppeteer.launch({headless: false})
page = await browser.newPage()
})
afterAll(async () => {
await browser.close()
})
You also then have to manually submit the form on the page, as in my experience uploadFile() doesn't do this. So in your case, for the WordPress Media Library single file upload form, the test would become:
it('Create test media', async () => {
// go to Media > Add New
await page.goto(`${env.WP_HOME}/wp/wp-admin/media-new.php`)
const display = await page.evaluate(() => {
const el = document.querySelector('#html-upload-ui')
return window.getComputedStyle(el).display
})
if (display !== 'block') {
// ensure we use the built-in uploader as it has an `input[type=file]`
await page.click('.upload-flash-bypass > a')
}
const input = await page.$('#async-upload')
await input.uploadFile(testMedia.path)
// now manually submit the form and wait for network activity to stop
await page.click('#html-upload')
await page.waitForNavigation({waitUntil: 'networkidle0'})
})

Related

how to remove chrome pop up from an automated test

I have this problem, I have some tests of e2e with puppeteer and they are failing because the chrome unsaved changes pop up appears and I do not want them to be saved, do you know how I could eliminate it?
I wanna click on the calcel btn of the pop up, or other way to make it work
Puppeteer has a way of handling alerts. You can Accept or Dismiss it.
https://pptr.dev/api/puppeteer.dialog
Example from the page above
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on('dialog', async dialog => {
console.log(dialog.message());
await dialog.dismiss();
await browser.close();
});
page.evaluate(() => alert('1'));
})();

How to get browser console logs when using Browser library in Robotframework

I'm using Robotframework and Browser library to automate some tasks on the web. I used to use Selenium, and with selenium there is a way to get the logs, for example in the case of a failure:
driver = webdriver.Remote()
logs = driver.get_log('browser')
I've been struggling to find a way to do the same exact thing using Playwright's Browser library. Is it possible?
Certainly. You can use the page.on('console') event to log what appears in the DevTools console. Here's an example of using debug library to do so.
Make sure to export DEBUG=playwright:console or you won't see anything.
Here's how to do it in JS:
const playwright = require('playwright');
const debugConsole = require('debug')('playwright:console');
(async () => {
const browser = await playwright.chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
await page.on('console', (msg) => {
if (msg && msg.text) {
if (typeof msg.text === 'function') {
debugConsole('PAGE LOG:', msg.text());
} else {
debugConsole('PAGE LOG:', msg.text);
}
} else {
debugConsole('PAGE LOG:', msg);
}
});
await page.goto('https://example.com', { waitUntil: 'networkidle' });
})();
And in python:
from playwright.sync_api import sync_playwright
def print_args(msg):
for arg in msg.args:
print(arg.json_value())
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.on("console", print_args)
page.goto("https://abrahamjuliot.github.io/creepjs/", wait_until="networkidle")
page.wait_for_timeout(5000)
browser.close()
If you are looking for more system-level stuff, there is also a dumpio launch parameter that you can set, which will cause Playwright to provide verbose logs on the actual launch of browser executable.

Writing a Testcafe test to assert a loading spinner is visible after making a fetch request

I have the following scenario:
Load page
Expect spinner is hidden
Type username Click search
Expect spinner display
After a few seconds delay, expect spinner to hide
Assert the right user details are displayed
Here is the working demo
I have mocked the network request in my test spec, but I am unable to understand how to assert spinner is visible after I click the search button
Here is my test spec:
import {Selector, RequestMock} from "testcafe";
import mockUser from "../mocks/mockUser.json";
var apiMocks = RequestMock()
.onRequestTo(/\/api\/users/)
.respond(mockUser, 200, {
'access-control-allow-credentials': "*",
'access-control-allow-origin': "*"
})
fixture `When a user is searched`
.page(`http://localhost:3000/`)
.requestHooks(apiMocks);
test("Should fetch user details", async t => {
const spinnerEl = Selector("[data-test-id='spinner']");
await t.expect(spinnerEl.exists).notOk();
await t
.typeText("[data-test-id='txt-search']", "foo")
.click("[data-test-id='btn-search']");
// This line does not work
// await t.expect(spinnerEl.exists).ok();
await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
})
I am new to TestCafe, could someone help me with this.
Thanks!
It is difficult to check whether the described spinner element is shown due to the following:
It is displayed only during a short period of time. This does not allow TestCafe to check it in time. Using mocks makes the spinner appear only for milliseconds.
TestCafe waits for all requests and does not perform any actions until XHR requests are completed. This means that assertions will not start until your request is finished.
However, it's still possible to work around the issue.
You can use MutationObserver and TestCafe ClientFunctions mechanism.
You can create your element observer using the ClientFunction. The observer will watch for the app element changes. If the spinner element appears the observer will be notified and set the window.spinnerWasShown variable to true.
After the button is clicked, you can check that the windows.spinnerWasShown variable is set to true.
Here is the full example:
import { Selector, RequestMock, ClientFunction } from "testcafe";
import mockUser from "../mocks/mockUser.json";
var apiMocks = RequestMock()
.onRequestTo(/\/api.github.com\/users/)
.respond(mockUser, 200, {
'access-control-allow-credentials': "*",
'access-control-allow-origin': "*"
});
fixture`When a user is searched`
.page(`http://localhost:3000/`)
.requestHooks(apiMocks);
const spinnerWasShown = ClientFunction(() => window.spinnerWasShown);
const observeSpinner = ClientFunction(() => {
var appEl = document.querySelector('.app');
const config = { attributes: true, childList: true };
const callback = function(mutationsList) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
for (var i =0; i < mutation.addedNodes.length; i++ )
window.spinnerWasShown = window.spinnerWasShown || mutation.addedNodes[i].className.indexOf('spinner') > -1;
}
}
};
const observer = new MutationObserver(callback);
observer.observe(appEl, config);
});
test("Should fetch user details", async t => {
const spinnerEl = Selector("[data-test-id='spinner']");
await t.expect(spinnerEl.exists).notOk();
await t.typeText("[data-test-id='txt-search']", "foo");
await observeSpinner();
await t.click("[data-test-id='btn-search']");
await t.expect(spinnerWasShown()).eql(true);
await t.expect(spinnerEl.exists).notOk();
await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
});

Control vue navigation from Testcafe

For testing Vue2 I use testcafe.
Command to run tests
testcafe firefox test/e2e/specs/ --app \"npm run dev\"
--app-init-delay 20000 -S -s test/e2e/screenshots
Test file
import { ClientFunction, Selector } from 'testcafe';
const devServer = require('../../../webpack/devServer');
fixture(`Getting Started`)
// Load the URL your development server runs on.
.page(`http://localhost:${devServer.port}/#/login`);
test('test login', async t => {
const userSelector = await new Selector('.login-squere input[type="text"]');
const passSelector = await new Selector('.login-squere input[type="password"]');
const logiIn = await new Selector('.login-squere button');
await t.typeText(userSelector, 'manager')
.typeText(passSelector, 'manager')
.click(logiIn);
});
I expect after .click(logiIn) site to route to /#/projects, but nothing happens
I added to test
await t.typeText(userSelector, 'manager')
.typeText(passSelector, 'manager')
.click(logiIn)
.navigateTo(`http://localhost:${devServer.port}/#/projects`);
And again no result. If I set .page to /#/projects it wil be rerouted to login.
So I can test only login page, because I cant make testcafe route Vue to next view.
This problem appears only if after login click we have ajax. TestCafe doesnt have request handler, so it is better to try something else for e2e
Try this one:
test('login', async t => {
await login(t);
});
and separate your code into a new function:
const login = async t => {
const userSelector = await new Selector('.login-squere input[type="text"]');
const passSelector = await new Selector('.login-squere input[type="password"]');
const logiIn = await new Selector('.login-squere button');
await t.typeText(userSelector, 'manager')
.typeText(passSelector, 'manager')
.click(logiIn);
};

How to upload file to server using react-native

I am developing a app where i need to upload an image to the server. Based on the image i get a response which i need to render?.
Can you please help me how to upload an image using react-native?.
There is file uploading built into React Native.
Example from React Native code:
var photo = {
uri: uriFromCameraRoll,
type: 'image/jpeg',
name: 'photo.jpg',
};
var body = new FormData();
body.append('authToken', 'secret');
body.append('photo', photo);
body.append('title', 'A beautiful photo!');
var xhr = new XMLHttpRequest();
xhr.open('POST', serverURL);
xhr.send(body);
My solution is using fetch API and FormData.
Tested on Android.
const file = {
uri, // e.g. 'file:///path/to/file/image123.jpg'
name, // e.g. 'image123.jpg',
type // e.g. 'image/jpg'
}
const body = new FormData()
body.append('file', file)
fetch(url, {
method: 'POST',
body
})
I wrote something like that. Check out https://github.com/kamilkp/react-native-file-transfer
I have been struggling to upload images recently on react-native. I didn't seem to get the images uploaded. This is actually because i was using the react-native-debugger and network inspect on while sending the requests. Immediately i switch off network inspect, the request were successful and the files uploaded.
I am using the example from this answer above it works for me.
This article on github about the limitations of network inspect feature may clear things for you.
Just to build on the answer by Dev1, this is a good way to upload files from react native if you also want to show upload progress. It's pure JS, so this would actually work on any Javascript file.
(Note that in step #4 you have to replace the variables inside the strings with the type and file endings. That said, you could just take those fields out.)
Here's a gist I made on Github: https://gist.github.com/nandorojo/c641c176a053a9ab43462c6da1553a1b
1. for uploading one file:
// 1. initialize request
const xhr = new XMLHttpRequest();
// 2. open request
xhr.open('POST', uploadUrl);
// 3. set up callback for request
xhr.onload = () => {
const response = JSON.parse(xhr.response);
console.log(response);
// ... do something with the successful response
};
// 4. catch for request error
xhr.onerror = e => {
console.log(e, 'upload failed');
};
// 4. catch for request timeout
xhr.ontimeout = e => {
console.log(e, 'cloudinary timeout');
};
// 4. create formData to upload
const formData = new FormData();
formData.append('file', {
uri: 'some-file-path', // this is the path to your file. see Expo ImagePicker or React Native ImagePicker
type: `${type}/${fileEnding}`, // example: image/jpg
name: `upload.${fileEnding}` // example: upload.jpg
});
// 6. upload the request
xhr.send(formData);
// 7. track upload progress
if (xhr.upload) {
// track the upload progress
xhr.upload.onprogress = ({ total, loaded }) => {
const uploadProgress = (loaded / total);
console.log(uploadProgress);
};
}
2. uploading multiple files
Assuming you have an array of files you want to upload, you'd just change #4 from the code above to look like this:
// 4. create formData to upload
const arrayOfFilesToUpload = [
// ...
];
const formData = new FormData();
arrayOfFilesToUpload.forEach(file => {
formData.append('file', {
uri: file.uri, // this is the path to your file. see Expo ImagePicker or React Native ImagePicker
type: `${type}/${fileEnding}`, // example: image/jpg
name: `upload.${fileEnding}` // example: upload.jpg
});
})
In my opinion, the best way to send the file to the server is to use react-native-fs package, so install the package
with the following command
npm install react-native-fs
then create a file called file.service.js and modify it as follow:
import { uploadFiles } from "react-native-fs";
export async function sendFileToServer(files) {
return uploadFiles({
toUrl: `http://xxx/YOUR_URL`,
files: files,
method: "POST",
headers: { Accept: "application/json" },
begin: () => {
// console.log('File Uploading Started...')
},
progress: ({ totalBytesSent, totalBytesExpectedToSend }) => {
// console.log({ totalBytesSent, totalBytesExpectedToSend })
},
})
.promise.then(({ body }) => {
// Response Here...
// const data = JSON.parse(body); => You can access to body here....
})
.catch(_ => {
// console.log('Error')
})
}
NOTE: do not forget to change the URL.
NOTE: You can use this service to send any file to the server.
then call that service like the following:
var files = [{ name: "xx", filename:"xx", filepath: "xx", filetype: "xx" }];
await sendFileToServer(files)
NOTE: each object must have name,filename,filepath,filetype
A couple of potential alternatives are available. Firstly, you could use the XHR polyfill:
http://facebook.github.io/react-native/docs/network.html
Secondly, just ask the question: how would I upload a file in Obj-C? Answer that and then you could just implement a native module to call it from JavaScript.
There's some further discussion on all of this on this Github issue.
Tom's answer didn't work for me. So I implemented a native FilePickerModule which helps me choose the file and then use the remobile's react-native-file-transfer package to upload it. FilePickerModule returns the path of the selected file (FileURL) which is used by react-native-file-transfer to upload it.
Here's the code:
var FileTransfer = require('#remobile/react-native-file-transfer');
var FilePickerModule = NativeModules.FilePickerModule;
var that = this;
var fileTransfer = new FileTransfer();
FilePickerModule.chooseFile()
.then(function(fileURL){
var options = {};
options.fileKey = 'file';
options.fileName = fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType = 'text/plain';
var headers = {
'X-XSRF-TOKEN':that.state.token
};
options.headers = headers;
var url = "Set the URL here" ;
fileTransfer.upload(fileURL, encodeURI(url),(result)=>
{
console.log(result);
}, (error)=>{
console.log(error);
}, options);
})
Upload Files : using expo-image-picker npm module. Here we can upload any files or images etc. The files in a device can be accessed using the launchImageLibrary method. Then access the media on that device.
import * as ImagePicker from "expo-image-picker";
const loadFile = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
aspect: [4, 3],
});
return <Button title="Pick an image from camera roll" onPress={loadFile} />
}
The above code used to access the files on a device.
Also, use the camera to capture the image/video to upload by using
launchCameraAsync with mediaTypeOptions to videos or photos.