Is there any headless browser for dart? Or a wrapper for something like selenium? My goal is to use the browser for automated end-user testing for a website i wrote years ago. Now i need to make few changes on the site. Since it contains specific business logic, i would like to write some quick tests for the site without refactoring or modifying it for unit-tests, before i make those changes.
It seems like a fun introduction to get more familiar with dart ecosystem as well.
You can use Chrome or Dartium and drive it with ChromeDriver and the webdriver package
Here is a quick example:
import 'dart:convert';
import 'dart:io';
import 'package:webdriver/io.dart';
main() async {
// Start the ChromeDriver process
Process chromeDriverProcess = await Process
.start('chromedriver', ['--port=4444', '--url-base=wd/hub']);
await for (String browserOut in const LineSplitter()
.bind(UTF8.decoder.bind(chromeDriverProcess.stdout))) {
if (browserOut.contains('Starting ChromeDriver')) {
break;
}
}
// Connect to it with the webdriver package
WebDriver driver = await createDriver(
uri: Uri.parse('http://localhost:4444/wd/hub/'),
desired: Capabilities.chrome);
// Go to your page
await driver.get('http://stackoverflow.com');
//TODO: write your tests
print(await driver.execute('return navigator.userAgent', []));
// Take a simple screenshot
String screenshot = await driver.captureScreenshotAsBase64();
new File('stackoverflow.png').writeAsBytesSync(BASE64.decode(screenshot));
driver.quit();
chromeDriverProcess.kill();
}
It is not totally "headless" but it is easy to make it work on server like Travis-CI with this config:
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
content-shell is a headless browser with Dart support like Dartium
(https://www.dartlang.org/install/mac)
and https://pub.dartlang.org/packages/webdriver can be used for Selenium test.
There is headless support for Chromium work-in-progress. When Dartium is upgraded to use this Chromium version, Dartium should be able to be run headless.
The Dart team is working on incremental JS compilation (DDC - Dart development compiler) which should allow to use Chrome as development browser. The headless mode (when available) can be used directly.
Depending on your use case and requirements, you might also be able to use puppeteer
For example (from the docs), you can take a screenshot of the page
void main() async {
// Start the browser and go to a web page
var browser = await puppeteer.launch();
var page = await browser.newPage();
// Setup the dimensions and user-agent of a particular phone
await page.emulate(puppeteer.devices.pixel2XL);
await page.goto('https://dart.dev', wait: Until.networkIdle);
// Take a screenshot of the page
var screenshot = await page.screenshot();
// Save it to a file
await File('example/_github.png').writeAsBytes(screenshot);
await browser.close();
}
You can also generate a PDF, take a screenshot of a node, interact with the page, evaluate JavaScript, and use the results from your Dart code, and more.
Related
I'm new to the Dart functions framework. My goal is to use this package to create several functions and deploy them to Cloud Run (in combination with Firebase, but I guess that's irrelevant to this question).
I've run the quick starts and I've read all of the contents in the docs.
The quick start mentions just one function at a time (e.g. Hello World, Cloud Events, etc..), like this:
import 'package:functions_framework/functions_framework.dart';
import 'package:shelf/shelf.dart';
#CloudFunction()
Response function(Request request) {
return Response.ok('Hello, World!');
}
But as you can see in the quickstarts only one function is handled in a project at a time. How about me wanting to deploy several functions? Should I:
Write several functions in the same project / file, so that the function framework compiles the 'server.dart` by itself
OR
Create a different functions_framework for each function?
Let me be more specific. Should I do the following (option 1 - which makes more sense to me):
import 'dart:math';
import 'package:functions_framework/functions_framework.dart';
import 'package:shelf/shelf.dart';
#CloudFunction()
Response function(Request request) {
return Response.ok('Hello, World!');
}
#CloudFunction()
Response function2(Request request) {
if (Random().nextBool()) {
return Response.ok('Hello, World!');
} else {
return Response.internalServerError();
}
}
Or should I build a different folder by running a build_runner for each function I need in my project?
Is there a difference and/or a best practice?
Thanks in advance.
EDIT. This question is related to the deployment on Cloud Run itself, and not just testing on my own PC. To test my own functions I did the following:
Run dart run build_runner build, so that it updates the server.dart file correctly (I can see that the framework does a lot behind the scenes and that the _nameToFunctionTarget is basically a router);
Run the server in two different terminals, like this: dart run bin/server.dart --port MYPORT --target MYFUNCTION (where MYPORT and MYFUNCTION are either 8080/8081 or function/function2 respectively).
I guess I'm just confused on how to correctly manage this framework once deployed on Cloud Run.
EDIT 2. I just gave up using Dart as a Serverless language or even a Backend language. There's just too much jargon even for the basic things. Any backend framework is either dead, or maintained by one single enthusiast guy (props to him!). This language has not yet received enough love from the Google Team / the community and at this moment in time is basically not possible to go fullstack on just Dart. It's a dream, but it can't be realized now. Furthermore, Dart hardly lacks a proper SDKs to use Firestore, etc., so Firebase isn't an option. I find it easier to just learn NodeJS and exploit the Firebase support for Firebase Functions written in NodeJS, and I'll wait for more support in there in the future, if there ever will be.
The documentation is a bit sparse right now (and I'm new to it also! I couldn't find any good examples, so here goes...)
You can only have a single function that is served. It should be
named 'function' (the type and name can be overriden, see the
cloudevent example dartfn generate cloudevent)
You 'could' have many of these deployed so that each does a specific thing, such as processing cloudevents above, but most people
want something more REST-like (see next)
You need to attach a Router() so that you can have the single entry point (function) handled by specific logic in your code.
Example for Rest
add to pubspec.yaml (in dependencies:) shelf_router: ^1.1.2
delegate the #CloudFunction to use the Router()
functions.dart
import 'package:functions_framework/functions_framework.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
Router app = Router()
..get('/health', (Request request) {
return Response.ok('healthy');
})
..get('/user/<user>', (Request request, String user) {
// fetch the user... (probably return as json)
return Response.ok('hello $user');
})
..post('/user', (Request request) {
// convert request body to json and persist... (probably return as json)
return Response.ok('saved the user');
});
#CloudFunction()
Future<Response> function(Request request) => app.call(request);
Being completely new to webdriver IO, wondering how do I update an existing test cycle (with specific name format) in Jira. I am running the test suite on 3 different browsers and have separate test cycles for these cycles in Jira. After execution, I want the suite to update these cycles with the status and screenshots for each browsers respectively. ANy help is much appreciated.
PS: At the moment I have a function that creates a new test cycle for each execution.
there are no plugins for zephyr scale integration till now, but you can use zephyr api to update your execution results .
I've created a Nodejs lib for creating testruns and reporting results back to Zephyr scale.
Maybe they can help you on your way.
If you have any questions or feedback, let me know!
https://www.npmjs.com/package/#dbouckaert/zephyr-scale-reporter
Example: get all testcases for a project
/**
* This function will get all testcases for a certain project and add them to variables.testCasesArray
* #returns {void}
*/
export const getAllTestcases = async (): Promise<void> => {
await request(variables.url)
.get(`/rest/tests/1.0/project/${variables.projectId}/testcases`)
.auth(variables.username, variables.password)
.expect(200)
.then((res) => {
variables.testCasesArray = res.body.testCases;
});
};
For example, here is a simple dart code:
#import('dart:io');
main() {
var server = new HttpServer();
server.listen('127.0.0.1', 8080);
server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
response.outputStream.write('Hello, world'.charCodes());
response.outputStream.close();
};
}
when the web server print the 'Hello, world', I would like to run a progress to run a
long heavy task, but don't want to it blocking the current process. May I know how to handle it? Thanks.
I tried with Process.run and Process.start with no success.
From you comment I can tell there are a misunderstanding of how Dart works spawning external processes. When you spawn a process in Dart it is by default running so the Dart program and the external program are running separate (so in different processes) and the Dart program can execute other stuff. You can then await for the result from the program (e.g. when it closes).
Therefore it does not make much sense to run the process with "&" as parameter (I guess this was an attempt to tell it should run separately from the Dart program).
But, since you are spawning another Dart program your should also consider using an Isolate which can execute both your own method on another thread or run external code by using:
https://api.dart.dev/stable/2.6.0/dart-isolate/Isolate/spawnUri.html
I'm working on a Browser extension/add-on. We have it working in Chrome, so I'm trying to get it working in Firefox.
I've gotten my add-on to load in Firefox Developer Edition 49.0a2 (2016-07-25).
My extension involves a content_script set to run_at: document_start, so it can inject a script tag before other page scripts run, so it can make an object globally available to websites.
This has seemed to work fine in Chrome, but in Firefox it has proven to be a bit of a race condition, with other page resources loading first most of the time.
Is there a strategy to load a content script in a way that it can inject & load a script before any other page scripts run?
When I add logs, I can isolate what is happening pretty nicely. In this example content-script:
// inject in-page script
console.log('STEP 1, this always happens first')
var scriptTag = document.createElement('script')
scriptTag.src = chrome.extension.getURL('scripts/inpage.js')
scriptTag.onload = function () { this.parentNode.removeChild(this) }
var container = document.head || document.documentElement
// append as first child
container.insertBefore(scriptTag, container.children[0])
Now if the file scripts/inpage.js simply runs a log, like
console.log('STEP 2, this should always run second')
And I visit a page with a script like this:
console.log('Step 3, the page itself, should run last')
In practice, Step 2 and Step 3 run in a non-deterministic order.
Thanks a lot!
I have Firefox-compatible version of the script in a public repository on a special branch if you dare to try it yourself: https://github.com/MetaMask/metamask-plugin/tree/FirefoxCompatibility
An dynamically inserted script with an external source (<script src>) does not block the execution of scripts, so there is no guarantee that your script would load. If your extension worked in Chrome, it was just by sheer luck.
If you really want to run some script before the rest, you have to run it inline:
var actualCode = `
// Content of scripts/inpage.js here
`;
var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();
Ideally, your build script would read scripts/inpage.js, serialize it to a string and put it in the actualCode variable. But if inpage.js is just a few lines of code, then the above can be used.
Note that you should not inject code in the web page unless it is absolutely necessary. The reason for that is that the execution environment of the page is untrusted. If you inject at document_start, then you can save functions and (prototype) methods that use for later (in a closure), but very careful coding is required.
If your content script is not generated by a build script and you still want to keep the scripts separate, then you can also use synchronous XMLHttpRequest to fetch the script. Synchronous XHR is deprecated for performance reasons, so use it at your own risk. Extension code is typically bundled with your extension, so the use of sync xhr should be low-risk:
// Note: do not use synchronous XHR in production!
var x = new XMLHttpRequest();
x.open('GET', chrome.runtime.getURL('scripts/inpage.js'), false);
x.send();
var actualCode = x.responseText;
var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();
If you are using a bootstrap.js based addon you can use a framescript and DOMWindowCreated to work with the document before even the HTML DOM (past basics of document.body etc) renders - https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment#Events - the innerHTML will be available but no script would have executed. You can put your inline script at the top as #Rob mentioned.
I was working on an app with Phonegap + React.js and Socket.io. However, then React-Native got released and the native feel is amazing.
I tried getting socket.io-client working with React Native, but unfortunately without much success. I did some research and I'm getting the exact same errors as described in this issue: https://github.com/facebook/react-native/issues/375
The comments on the issue said to try and use the fetch API to fetch JS modules, but I think I'm doing this wrong:
var socketScript;
fetch('https://cdn.socket.io/socket.io-1.2.0.js')
.then(function(response) {
socketScript = response._bodyText;
}).done(function() {
var socket = socketScript.io();
});
This returns an undefined is not a function.
Is there any way to make socket.io-client work with React Native? Or am I looking at this the wrong way? Perhaps there are other, better suited solutions?
For those like me stumbling across this question looking how to integrate socket.io with react native.
Since React Native has supported websockets for a short time now, you can now set up web sockets really easily with Socket.io. All you have to do is the following
npm install socket.io-client
first import react-native
assign window.navigator.userAgent = 'react-native';
import socket.io-client/socket.io
in your constructor assign this.socket = io('localhost:3001', {jsonp: false});
So in all it should look like this after npm installing socket.io-client:
import React from 'react-native';
// ... [other imports]
import './UserAgent';
import io from 'socket.io-client/socket.io';
export default class App extends Component {
constructor(props) {
super(props);
this.socket = io('localhost:3001', {jsonp: false});
}
// now you can use sockets with this.socket.io(...)
// or any other functionality within socket.io!
...
}
and then in 'UserAgent.js':
window.navigator.userAgent = 'react-native';
Note: because ES6 module imports are hoisted, we can't make the userAgent assignment in the same file as the react-native and socket.io imports, hence the separate module.
EDIT:
The above solution should work, but in the case it doesn't try create a separate socketConfig.js file. In there import anything that is needed, including const io = require('socket.io-client/socket.io'); and having window.navigator.userAgent = 'react-native'; BEFORE requiring socket.io-client. Then you can connect your socket there and have all listeners in one place. Then actions or functions can be imported into the config file and execute when a listener receives data.
Now, if you want to use socket.io in your RN app, you must use this code:
if (!window.location) {
// App is running in simulator
window.navigator.userAgent = 'ReactNative';
}
// This must be below your `window.navigator` hack above
const io = require('socket.io-client/socket.io');
const socket = io('http://chat.feathersjs.com', {
transports: ['websocket'] // you need to explicitly tell it to use websockets
});
socket.on('connect', () => {
console.log('connected!');
});
Big thanks for Eric Kryski.
import { io } from 'socket.io-client'
const socket = io(`${SOCKET_URL}:${SOCKET_PORT}`)
Important! SOCKET_URL should be your local IP address, not localhost or 127.0.0.1.
To check your local IP:
Mac / Linux: run ifconfig in terminal
Windows: run ipconfig --all in shell
Should be something like: const socket = io('http://10.0.1.6:3000', {transports: ['websocket']})
Short of a polyfill for the WebSocket API, you can create a native module that makes use of web-sockets and send events to Javascript using eventDispatcher.
On the Javascript side, you would subscribe to these events using DeviceEventEmitter.addListener.
For more information on using native modules, see the react-native doc on the topic
Edit Feb 2016: React Native now supports Web Sockets so some of this advice is invalid.
You've misinterpreted the Github issue I'm afraid. In it, aackerman says:
For this specific case you'll likely want to use the fetch API which
is provided by the environment.
He doesn't say that you should use the fetch API to grab remote JS modules. What he's suggesting is that the fetch API be used in place of the built-in Node.JS request module, which isn't available in React Native.
Let's look at your code:
socketScript = response._bodyText;
var socket = socketScript.io();
Think about this for a second - socketScript isn't a JavaScript object, it's a string - therefore how can you call the io method on it?
What you'd really need to do is parse _bodyText before using it (in a browser you could use eval), but then you'd still have the problem that while React Native has a polyfill for XHR and the fetch API, it doesn't yet have one for the WebSocket API. Unless I'm mistaken, this means you're stuck.
I suggest opening a Github issue to request a WebSocket API polyfill and ask for the thoughts of the community. Someone might have a workaround.
Although you can use socket.io-client lib, the community is complaining about compatibility issues with most versions (I did experience some). It works, but now I'm afraid to upgrade the lib because I need to verify the compatibility of the next version to my server's version and react-native's version!
It seems that a lot of people miss react's own implementation of Websockets! I really recommend you use this instead of socket.io-client. It is very similar in usage:
var ws = new WebSocket('ws://host.com/path');
ws.onopen = () => { // connection opened ws.send('something'); // send a message};
ws.onmessage = (e) => { // a message was received console.log(e.data);};
ws.onerror = (e) => { // an error occurred console.log(e.message);};
ws.onclose = (e) => { // connection closed console.log(e.code, e.reason);};
Finally found it.
Client
import { io } from "socket.io-client/build/index"
io("ws://<LOCAL HOME NETWORK IP>:<PORT ON SERVER>")
Server
import express from "express"
import http from "http"
import * as SocketIO from "socket.io"
const app = express()
const server = new http.Server(app)
const io = new SocketIO.Server(server)
const port = 8000
io.on("connection", socket => {
console.log("CONNECTIONS")
}
may be this will through error
import io from "socket.io-client/socket.io"
Then just add below line....
import io from "socket.io-client/dist/socket.io";
then in componenDidMount or useEffect function just add below line.Never use it under constructor of class component.
var socket = io("https://localhost.com:3000", { jsonp: false });
// client-side
socket.on("chat_message", (msg) => {
console.log(msg);
});
2022 Answer
In 2022 you can easily just use the latest version of socket.io-client with React Native.
npm install socket.io-client
import io from 'socket.io-client';
Right now there isn't a good hook based socketIO libary that I've been able to make work with RN but it's pretty straightforward to roll out your own custom hook depending on your needs. IE
function useWebsocket(url) {
const [connected, setConnected] = useState(false);
const [socket, setSocket] = useState(null);
useEffect(()=>{
const newSocket = io(url);
newSocket.on('connect', ()=>setConnected(true));
newSocket.on('disconnect', ()=>setConnected(false));
setSocket(newSocket);
}, [])
return {
connected,
socket,
}
}
Something like this can get you started. This would open a socket for each component that calls the hook, which can work well if you just need one component with one connection. Sharing the connection across components gets a little more hairy but it isn't too bad.
The connected state is really useful for letting your user know the status of connections and stuff like that.
But yeah, point is you can just install it and use it in your component. Don't use it in the body of your functional components