BrowserStack is a powerful platform for testing web sites against the most
current and modern browser. So far so good.
BrowserStack also provides an API
The API has the concept of a worker representing a specific browser (version) loading a particular URL.
What useful things can I do with such a worker instance?
How would one integrate such a worker with Selenium tests?
How would one integrate such a worker with unittests (Python)?
How would one use such a worker e.g. for testing if a particular website with a video player would actually load and play a video (e.g. for cross-browser video testing)?
Current API opens your provided url in all platform/browser combinations.
So, if you open an HTML page with lot of JS tests, you need to be using tool like yeti/testswarm/js-test-driver which automatically fetch results from browser.
Another example of using BrowserStack API is http://ryanseddon.github.com/bunyip/
Sample integration with Jenkins: http://github.com/jquery/testswarm/wiki/Automated-Distributed-Continuous-Integration-for-JavaScript
For local JS testing, you will need to use tools like localtunnel to get a public url for your local servers.
One of the most useful capabilities of the current BrowserStack API is to allow you to mark a session as a failed test.
Like any Selenium hub/node system, BrowserStack doesn't know why you're sending commands to the browser. It just runs the commands you request. Consequently, it has no way to know when a test fails.
But you can use the API to tell it that a test failed, so that the session gets marked as failed in the BrowserStack UI. Then you can filter on just the failed sessions to investigate them.
This is in Java, not Python, but here's some sample code that shows how to update sessions to reflect that they represent failed tests. You just pass in the Selenium session IDs (which you need to save as you run the test in question) and the exception you got when the test failed.
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.unblu.automation.support.settings.Prop;
import com.unblu.automation.support.settings.Settings;
import org.openqa.selenium.remote.SessionId;
public class BrowserStackUpdater {
private void markSessionAsFailed(SessionId sessionId, Throwable e) {
var url = "https://api.browserstack.com/automate/sessions/" + sessionId + ".json";
try {
var userName = "BROWSERSTACKUSERNAMEHERE";
var key = "BROWSERSTACKKEYHERE";
var result = Unirest.put(url)
.basicAuth(userName, key)
.field("status", "failed")
.field("reason", e.toString())
.asString();
System.out.println("Marking test failed; reply from BrowserStack: " +
result.getStatus() + " : " + result.getBody());
}
catch (UnirestException ue) { ue.printStackTrace(); }
}
public void markTestFailedInBrowserStack(Iterable<SessionId> sessionIds, Throwable e) {
var env = Settings.getString(Prop.runEnvironment);
if (env.equals("BrowserStack")) {
for (var sid : sessionIds) {
markSessionAsFailed(sid, e);
}
}
}
}
Related
Issue
Sending Web Push Notifications to APNS returns a "200" OK status, but the notification never reaches my Mac computer.
Requesting permission to send Web Push Notifications
Created "Website push id" in developer.apple.com, wrote scripts to dynamically create the push package with all the appropriate hashes and signature.
In Angular app I request permission and get the device token per this:
checkSafariPushPermission() : void{
if ('safari' in window && 'pushNotification' in window.safari) {
this.checkAppleWebPushNotificationsRemotePermission(window.safari.pushNotification.permission(this.appleWebsitePushId));
}
}
checkAppleWebPushNotificationsRemotePermission(permissionData: AppleWebPushNotificationPermissionsData) : void {
let webServiceURL = this.globalVarsService.apiUrl + 'apple_pushnotifications/';
if (permissionData.permission === 'default') {
// This is a new web service URL and its validity is unknown.
window.safari.pushNotification.requestPermission(
webServiceURL, // The web service URL.
this.appleWebsitePushId, // The Website Push ID.
{ userId: this.globalVarsService.userId }, // Data that you choose to send to your server to help you identify the user.
function(permissionData: AppleWebPushNotificationPermissionsData) {
this.permissionSet(permissionData);
}
);
}
else if (permissionData.permission === 'denied' || permissionData.permission === 'granted') {
this.permissionSet(permissionData);
}else{
this.utilsService.doAlert("There was a problem processing your permissions selection with Apple.");
}
};
permissionSet(permissionData: AppleWebPushNotificationPermissionsData) : void{
console.log("Permission " + permissionData.permission)
console.log("Token", permissionData.deviceToken)
}
On allowing permission this returns the permissionData.deviceToken correctly and the Push Package is added to my Mac so that I can see the web app listed in both Safari "Preferences > Websites > Notifications" where is shows with "Allow" selected. And also in the Notification Centre where I have all the options for "Banners" "Badges" etc.
So the permissions step appears to be working properly.
Sending Web Push Notifications
In developer.apple.com I created a Apple Push Notifications service key.
In python using the gobiko.apns library I compose
client = APNsClient(
team_id=APPLE_TEAM_ID,
bundle_id=CERTIFICATE_NAME_FROM_P12_CERTIFICATE_ABOVE,
auth_key_id=APPLE_WEB_PUSH_NOTIFICATION_APNS_KEYID,
auth_key_filepath=APPLE_WEB_PUSH_NOTIFICATION_APNS_KEY_PATH,
use_sandbox=False
)
client.send_message(
'DEVICE_TOKEN_HERE',
"This is my message"
)
The code executes without error and if I print the response from APNS it gives a "200" status ok. No notification shows on the Mac computer and I can't see any way to debug.
I am unsure if it is correct to use the "name" of the Website Push certificate from developer.apple.com as the bundle_id.
I've also checked the do not disturb settings.
I am aware that delivery is not guaranteed, but I'm not sure if it's working at all.
Any debugging guidance would be appreciated. Thank you.
I am using the following code to login to our Salesforce orgs, the code works perfectly as long as it is connecting to prod org, but once I change the endpoint from "https://login.salesforce.com/services/Soap/u/35.0" to "https://test.salesforce.com/services/Soap/u/35.0" to connect to our test org, it failed and showed my exceptionCode='INVALID_LOGIN'.
from runcmd.java_helper import jvm
from runcmd.logging_helper import get_logger
LOGGER = get_logger(__name__)
ENDPOINT = "https://test.salesforce.com/services/Soap/u/35.0"
def authenticate(username, password, security_token) -> dict:
"""
Authenticates against salesforce.
"""
return {
"username": username,
"password": password,
"security_token": security_token
}
def _build_sf_connection(spark, sf_obj):
config = jvm(spark).com.sforce.ws.ConnectorConfig()
config.setUsername(sf_obj["username"])
config.setPassword(sf_obj["password"] + sf_obj["security_token"])
config.setAuthEndpoint(ENDPOINT)
config.setServiceEndpoint(ENDPOINT)
LOGGER.info("Username=%s" % sf_obj["username"])
return jvm(spark).com.sforce.soap.partner.Connector.newConnection(config)
I checked the login info and they are all correct but I noticed even though I have changed the endpoint in the code to test.salesforce, it still trying to connect through login.salesforce .
20/05/01 06:13:58 INFO SFConfig: loginURL : https://login.salesforce.com/services/Soap/u/35.0
20/05/01 06:14:02 ERROR SFConfig: Exception while creating connection
[LoginFault [ApiFault exceptionCode='INVALID_LOGIN'
exceptionMessage='Invalid username, password, security token; or user locked out.'
extendedErrorDetails='{[0]}'
You must have something else that sets login.salesforce.com, maybe java application has it hardcoded in JAR or defined in their own config files... any special reason why it's done through java? have you experimented with https://pypi.org/project/simple-salesforce/ or https://pypi.org/project/pyforce/ and deemed them unfit?
Looking closer at com.sforce.soap.partner... hm, looks like "Partner WSDL" that's been consumed, had Java classes generated out of it (go to Setup -> API). You could inspect these classes and see if there's anything built in that lets you override at runtime the code generated based on that snippet at bottom of the WSDL.
<service name="SforceService">
<documentation>Sforce SOAP API</documentation>
<port binding="tns:SoapBinding" name="Soap">
<soap:address location="https://login.salesforce.com/services/Soap/u/48.0"/>
</port>
</service>
If it was just generated and left to its own devices and nobody wants to touch Java... Hm, check if you can have 2 compiled JAR files sitting next to each other and you load test or prod version to Python as needed?
But it really seems like roundabout way of doing things and API v 35 was released ~3 years ago, definitely worth checking your options now.
I'm working on a web interface for a project and need to plot a realtime chart with some data I'll get from an API I'm building. The API runs on http://SERVER_IP:5000/signal and my web interface on http://SERVER_IP:80/, both of them with Flask.
I was having CORS issues at the very beginning and then found out about Flask-CORS. I tried to implement that and now I'm getting no errors on the browser console, but no messages either! I've added some console messages for debugging, but it doesn't seem alright.
On my dashboard, I try to reach the API with the following code:
const source = new EventSource("http://SERVER_IP:5000/signal", {withCredentials: true});
console.log ("Things started!!!");
source.onmessage = function (event) {
console.log ("An event's just happened");
// parse data and do stuff
}
and in my API, I set Flask-CORS like this:
self.__cors = CORS(self.__app, supports_credentials = True)
and the route like this:
#self.__app.route('/signal')
def get_signal():
import json
def get_data():
while True:
json_data = json.dumps(...)
yield "{}\n\n".format(json_data)
time.sleep(1)
return Response(get_data(), mimetype='text/event-stream')
Then, when I open my web browser and open console, I can see the "Things started!!!" message, but no "An event's just happened" and no data on the chart.
I was malforming my response. As I found out here, my "get_data" method should yield "data: ...", so after I added the "data: " after my data, things started to work pretty well.
I've written a Python action on Bluemix OpenWhisk, and I need to call another action (actually a binding to a public package) from within this action. A sequence won't do it, because I need to call it a varying number of times with different parameters depending on the input.
How to invoke openwhisk action within openwhisk platform on bluemix? mentions how to do it from JavaScript, but the OpenWhisk package doesn't seem to be available for Python.
Actions can be invoked using a HTTP request to the platform API. The Python runtime in OpenWhisk includes the requests library for making HTTP calls.
Here's an example of an action that calls another (child) in the same namespace.
import os
import requests
APIHOST = os.environ.get('__OW_API_HOST')
NAMESPACE = os.environ.get('__OW_NAMESPACE')
USER_PASS = os.environ.get('__OW_API_KEY').split(':')
def main(params):
action = 'child'
url = APIHOST + '/api/v1/namespaces/' + NAMESPACE + '/actions/' + action
response = requests.post(url, data=params, params={'blocking': 'true'}, auth=(USER_PASS[0], USER_PASS[1]))
print(response.json())
return {"text": "invoked!"}
Swagger documentation for full API is available here.
There is an open issue to create a Python client library to make this easier.
We have created and closed a large number of Projects in Rally over the years. Because you can't actually delete Projects entirely, I've found the need to re-open the closed Projects, modify some artifacts, and reclose the project. A simple example of what I'm trying to do is reflected in this bit of Python:
resp = session.get('https://rally1.rallydev.com/slm/webservice/v2.0/project/' + ObjectID, auth=HTTPBasicAuth(user, password))
state = resp.json()["Project"]["State"]
if state == "Closed":
info = { "State": "Open" }
resp = session.post('https://rally1.rallydev.com/slm/webservice/v2.0/project/' + ObjectID + '?key=' + token, auth=HTTPBasicAuth(user, password), data=json.dumps(info))
print resp.content
So if a project's "State" is "Closed", POST a JSON object to the API URL of the Project setting it to "Open".
It doesn't work. I get this response:
{
"OperationResult": {
"Errors": [
"Cannot set attribute on a com.rallydev.webservice.json.JSONSingleProperty"
],
"Warnings": [],
"_rallyAPIMajor": "2",
"_rallyAPIMinor": "0"
}
}
Is there another way to open/close a Project via the Rally WS API?
There may be two issues.
First, there was a performance optimization made a couple of years ago that limited queries to open projects. At this point, the only way to get a list of closed projects is on the Projects page for a given workspace, in the UI. When we query for projects, WS API returns only open projects. Try without checking for this condition state == "Closed"
However as long as the project endpoint is accessed directly, it should be possible to reopen a project. I did not try it with Python, but using a browser REST Client I re-opened a project as follows:
a) got security token from security endpoint:
https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize
b) appended the token to the request:
Endpoint:
https://rally1.rallydev.com/slm/webservice/v2.0/project/14304671845?key=b2c8aa01-...
Payload:
{"Project":{
"State":"Open"
}}
This works.
Second, security token has to be appended to the post request, but it is not enough. Please make sure that you maintain the session cookie, since unlike the scenario in the browser REST client, where the browser maintains the session automatically, in your scenario this is not the case. See this StackOverflow post.