@samknows/web-sdk v2.0.9
SamKnows Web SDK: web-based speed test
Overview
The SamKnows Web SDK provides a way for users to manually run speed tests against SamKnows measurement servers without the need for a hardware agent (such as a Whitebox or embedded solution) or an app on their mobile device.
This library allows you to completely configure the appearance and behaviour of the speed test.
Table of contents
Install
You can either use a javascript package manager or a CDN to add the WebSDK on any HTML page and access the global SpeedTest
variable..
The project only contains 3 files: README
, speed-test.js
and speed-test.d.ts
. The speed-test.js
file is bundled using the UMD syntax, so you can import it using a bundler tool such as RequireJS, Browserify or Webpack, or you can add the file directly to your site and use the global SpeedTest
variable.
- npm
$ npm install @samknows/web-sdk --save
- yarn
$ yarn add @samknows/web-sdk
- CDN
<script src="https://unpkg.com/@samknows/web-sdk"></script>
Licence key
You will need a valid licence key in order to use this library. Please contact your SamKnows account manager to request a licence key.
Once you have a licence key, you will need to pass it to the WebSDK via the options (see How to use
Web SDK API
Supported tests
Due to the nature of the browser, the web test only runs a subset of the tests available in the embedded SDK or mobile apps:
- Latency
- Jitter (derived from the latency test)
- Download speed
- Upload speed
Find fastest server
The latency test is ran on all test server to find which one is the fastest. results.fastestServer
is used on some clients website to show which one it was tested against.
Running all tests in one go
The Web SDK is the most flexible way of using the web-based speed test, handling only the logic of running the test and leaving the interface completely up to you.
To initialise and start the test, create a new instance of the SpeedTest
object imported from the library:
// This line is not needed if you're using the CDN
import SpeedTest from '@samknows/web-sdk';
const st = new SpeedTest({
global: {
licenceKey: 'YOUR-LICENCE-KEY'
}
});
You can pass options into the constructor (which will be covered shortly) but the defaults are usually reasonable.
The speed test will immediately start running. The returned object is an event emitter which can be used as follows:
st.on('progress', function (results) {
// results is an object containing the test results
});
The progress event is emitted whenever some data arrives from the server and the result of the currently in progress test is updated. This event can be emitted very often so can be a performance bottleneck if you try to animate the page on this event - it is more efficient to store the results to a variable and animate the page on requestAnimationFrame
instead.
st.on('progress-warmup', function (results) {
// results is an object containing the test results
});
The download and upload tests have warmup phases where they're downloading data, but the result isn't counted towards the final result. The progress-warmup event is emitted as if the test is running, but then the duration and bytes sent data is reset after the warmup phase and the test phase starts again from 0.
st.on('test-done', function (testName, results) {
// testName is the name of the test that just finished
// results is an object containing the test results
});
The test-done event is emitted when a test finishes running (and thus, when the next one begins). It is useful for updating the user interface between tests.
Unlike most of the other events, this one has two arguments - the first is the name of the test that just finished, and the second is the object containing results as in the other events.
st.on('done', function (results) {
// results is an object containing the test results
});
The done event is emitted once when the test has finished running and the tests have been cleaned up (e.g. ensuring that all connections are now closed).
st.on('error', function (err) {
// err is an Error object
});
The error event is emitted when the test fails to complete. This can be for any number of reasons - check the error message for more info. Usually it's because of a network problem.
Results ingestions
The speed test sends the results of the test back to the SamKnows ingestion server, configuration for which can be found in the ingestion
object. You will probably never need to change the ingestionApi
property, but you'll need to set the panelId
to the ID of the panel you want the results reporting to.
The st
object will emit a test-id
event with the ID of the stored test once the ingestion API returns successful - this will happen after the done event.
Running one test at a time
If you want to only run a specific test, in any order you want, you can set the immediate
global option to false when creating the web test:
const st = new SpeedTest({
global: {
licenceKey: 'YOUR-LICENCE-KEY',
immediate: false
},
});
This will find the nearest target right away and trigger the ready
event when ready to test. You can then run one of the 3 tests: download
, upload
, or latency
(includes jitter) in any order you want.
st.on('ready', () => {
st.run('download');
});
To give you more flexibility, you will need to ingest the results yourself.
You can use the same exact same events as above (progress
, test-done
, error
). Only the done
event will not be triggered as it indicates all tests ran and have been ingested (which doesn't happen for individual tests).
The results object
While the speed test is in progress, the results object will look something like this:
{
"inProgress": "download",
"latency": {
"round_trip_time": 6489,
"jitter": 919,
"minLatency": 5.570000001229346,
"duration": 88075,
"successes": 10,
"failures": 0,
"target": "10.0.0.106",
"percentage_complete": 100,
"utc_datetime": "2018-06-12T10:27:30.599Z"
},
"download": {
"bytes_sec": 6964165,
"mbps": 55.713320546338146,
"bytes_total": 69639840,
"duration": 9999740,
"successes": 1,
"failures": 0,
"target": "10.0.0.106",
"utc_datetime": "2018-06-12T10:27:42.607Z"
}
}
And when it is done, it will look something like this:
{
"latency": {
"round_trip_time": 53348,
"jitter": 27507,
"minLatency": 25.84000000206288,
"duration": 631070,
"successes": 10,
"failures": 0,
"target": "10.0.0.106",
"utc_datetime": "2018-06-12T10:28:19.779Z",
"percentage_complete": 100
},
"download": {
"bytes_sec": 6721736,
"mbps": 55.713320546338146,
"bytes_total": 69639840,
"duration": 9998540,
"successes": 1,
"failures": 0,
"target": "10.0.0.106",
"utc_datetime": "2018-06-12T10:28:31.778Z",
"percentage_complete": 100
},
"upload": {
"bytes_sec": 1381142,
"mbps": 11.66215590350929,
"bytes_total": 14286848,
"duration": 9679920,
"successes": 1,
"failures": 0,
"target": "10.0.0.106",
"utc_datetime": "2018-06-12T10:28:43.825Z",
"percentage_complete": 100
}
}
Some things to note:
- Some properties, such as
mbps
andbytes_sec
, are derived from other properties. successes
andfailures
don't really mean much on the download and upload tests.utc_datetime
is the time the test started: add the duration to get when the test finished.duration
is in microsecondsjitter
is in microsecondsround_trip_time
is the latency result in microseconds- The library does no rounding, leaving it completely up to the service consuming the data.
API options
You can pass options into the speed test by providing an object as the first argument to the SpeedTest
constructor:
const st = new SpeedTest({
global: {
testServer: ['n1-the1.samknows.com', 'n2-the1.samknows.com', 'n3-the1.samknows.com'],
licenceKey: 'YOUR-LICENCE-KEY'
},
download: {
duration: 7000,
},
});
The default options are as follows:
new SpeedTest({
global: {
targetsApi: '//speedtest-api.samknows.com/targets',
targetSet: undefined,
wsTestServerPort: 6501,
fetchTestServerPort: 80,
wsConcurrency: 8,
fetchConcurrency: 8,
warmupDuration: 2000,
immediate: true,
licenceKey: undefined
},
latency: {
packets: 10,
},
download: {
duration: 10000,
startChunkSize: 16 * 1024,
maxChunkSize: 1024 * 1024,
maxChunkDuration: 1000,
},
upload: {
duration: 10000,
startChunkSize: 16 * 1024,
maxChunkSize: 1024 * 1024,
maxChunkDuration: 1000,
},
ingestion: {
ingestionApi: 'https://ingestion-api.samknows.com',
panelId: undefined,
},
});
Each test looks at its own part of the object, then it looks at the global
object if it isn't defined. For example, when the download test wants to know how long to run for, it looks at config.download.duration
- it's defined, so it uses it. When it wants to know what server to test against, it looks at config.download.testServer
, but that isn't defined so it uses config.global.testServer
instead.
This means you can set configuration for all tests or configuration for individual tests depending on what your needs are.
You can override parts of the object or the whole object - the options you provide and the default options are merged using a deep merge strategy. Generally, most of the options can be left alone.
If you don't specify the testServer
option (either a single server or an array of servers - the latency test will calculate which to test against), the speed test will call the SamKnows targets API to calculate the nearest test servers to test against. You can specify which target set to use using the targetSet
option.
Licence agreement
Subject to the licence and terms agreed between SamKnows and the party.