Skip to content

Commit

Permalink
Add Safari 17 to Karma and remove some old browsers (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
Finesse authored Dec 11, 2023
1 parent d11635c commit 19dddc0
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 336 deletions.
104 changes: 7 additions & 97 deletions docs/browser_support.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,15 @@
# Browser support

The library supports all popular browsers.
We use the following command to determine which browsers to support:

```bash
npx browserslist "cover 96% in us, not IE < 11"
```
We aim to cover at least 99% of all users according to the Fingerprint Pro statistics.

At the moment, these browsers are:

- **Internet Explorer** 11 ([see the section below](#old-browsers-requirements))
- **Edge** 93+
- **Chrome** 49+
- **Firefox** 52+
- **Desktop Safari** 12.1+
- **Mobile Safari** 10.3+
- **Samsung Internet** 14.0+
- **Android Browser** 4.4+ ([see the section below](#old-browsers-requirements))
- **Edge** 105+
- **Chrome** 65+
- **Firefox** 75+
- **Desktop Safari** 12.1+
- **Mobile Safari** 12.0+
- **Samsung Internet** 14.0+

Other browsers will probably also work, but we don't guarantee it.

## Old browsers requirements

### `import()` support

If you use the "Browser ECMAScript module" installation methods, you may have an error.
Old browsers don't support [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import).
Replace it with a `<script>` tag:

```diff
+ // Note that we use iife.min.js with older browsers
+ <script src="https://openfpcdn.io/botd/v1/iife.min.js"></script>
<script>
- const botdPromise = import('https://openfpcdn.io/botd/v1')
- .then(BotD => BotD.load())
+ var botdPromise = BotD.load()

// ...
</script>
```

### Polyfills

Very old browsers like Internet Explorer 11 and Android Browser 4.4
require a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) polyfill to work.
Add a Promise polyfill before loading the BotD agent.
Examples for various installation methods:

#### Webpack/Rollup/NPM/Yarn

```bash
# Install the polyfill package first:
npm i promise-polyfill
# or
yarn add promise-polyfill
```

```diff
+ import 'promise-polyfill/src/polyfill'
import BotD from '@fingerprintjs/botd'

// ...
```

#### UMD

```diff
require(
[
'https://openfpcdn.io/botd/v1/umd.min.js',
+ 'https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js',
],
function (BotD) {
// ...
}
)
```

#### CommonJS syntax:

```diff
+ require('promise-polyfill/src/polyfill')
const BotD = require('@fingerprintjs/botd')

// ...
```

### Code syntax

Old browsers like IE11 don't support `const`, `let` and arrow functions (`=>`).
Use `var` and the classic function syntax instead:

```diff
- const botdPromise = BotD.load()
+ var botdPromise = BotD.load()

botdPromise
- .then(fp => fp.detect())
+ .then(function (fp) { return fp.detect() })
- .then(result => {
+ .then(function (result) {
// Handle the result
})
```
190 changes: 10 additions & 180 deletions karma.conf.ts
Original file line number Diff line number Diff line change
@@ -1,181 +1,11 @@
import { Config, CustomLauncher } from 'karma'
import { KarmaTypescriptConfig } from 'karma-typescript/dist/api/configuration'
import { karmaPlugin, setHttpsAndServerForKarma, BrowserFlags } from '@fpjs-incubator/broyster/node'

declare module 'karma' {
interface ConfigOptions {
karmaTypescriptConfig?: KarmaTypescriptConfig | undefined
}

interface Config extends ConfigOptions {
preset?: string
reporters: ConfigOptions['reporters']
}
}

// The shapes of these objects are taken from:
// https://github.com/SeleniumHQ/selenium/tree/d8ddb4d83972df0f565ef65264bcb733e7a94584/javascript/node/selenium-webdriver
// It doesn't work, trying to work it out with BrowserStack support. Todo: solve it with the support.
/*
const chromeIncognitoCapabilities = {
'goog:chromeOptions': {
args: ['--incognito'],
import { makeKarmaConfigurator } from '@fpjs-incubator/broyster/node'

module.exports = makeKarmaConfigurator({
projectName: 'BotD',
includeFiles: ['src/**/*.ts', 'tests/**/*.ts', 'test-dist/botd.min.js'],
configureCustom(karmaConfig) {
karmaConfig.set({
failOnEmptyTestSuite: false,
})
},
}
const firefoxIncognitoCapabilities = {
'moz:firefoxOptions': {
prefs: {
'browser.privatebrowsing.autostart': true,
},
},
}
*/

/*
* You can find values for any supported browsers in the interactive form at
* https://www.browserstack.com/docs/automate/javascript-testing/configure-test-run-options
* The keys are arbitrary values.
*
* Only Chrome is supported on Android, only Safari is supported on iOS: https://www.browserstack.com/question/659
*/
/* eslint-disable max-len */
// prettier-ignore
const browserstackBrowsers = {
Windows10_Chrome57: { platform: 'Windows', osVersion: '10', browserName: 'Chrome', browserVersion: '57', useHttps: true },
// Windows10_Chrome57_Incognito: { platform: 'Windows', osVersion: '10', browserName: 'Chrome', browserVersion: '57', ...chromeIncognitoCapabilities },
Windows11_ChromeLatest: { platform: 'Windows', osVersion: '11', browserName: 'Chrome', browserVersion: 'latest-beta', useHttps: true },
// Windows11_ChromeLatest_Incognito: { platform: 'Windows', osVersion: '11', browserName: 'Chrome', browserVersion: 'latest-beta, ...chromeIncognitoCapabilities },
Windows10_Firefox67: { platform: 'Windows', osVersion: '10', browserName: 'Firefox', browserVersion: '67', useHttps: true },
// Windows10_Firefox67_Incognito: { platform: 'Windows', osVersion: '10', browserName: 'Firefox', browserVersion: '67', ...firefoxIncognitoCapabilities },
Windows11_FirefoxLatest: { platform: 'Windows', osVersion: '11', browserName: 'Firefox', browserVersion: 'latest-beta', useHttps: true },
// Windows11_FirefoxLatest_Incognito: { platform: 'Windows', osVersion: '11', browserName: 'Firefox', browserVersion: 'latest-beta, ...firefoxIncognitoCapabilities },
Windows11_EdgeLatest: { platform: 'Windows', osVersion: '11', browserName: 'Edge', browserVersion: 'latest-beta', useHttps: true },
'OSX10.14_Safari12': { platform: 'OS X', osVersion: 'Mojave', browserName: 'Safari', browserVersion: '12', useHttps: true },
OSX12_Safari15: { platform: 'OS X', osVersion: 'Monterey', browserName: 'Safari', browserVersion: '15', useHttps: false },
OSX13_Safari16: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Safari', browserVersion: '16', useHttps: false },
OSX13_ChromeLatest: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Chrome', browserVersion: 'latest-beta', useHttps: true },
// OSX13_ChromeLatest_Incognito: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Chrome', browserVersion: 'latest-beta, ...chromeIncognitoCapabilities },
OSX13_FirefoxLatest: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Firefox', browserVersion: 'latest-beta', useHttps: true },
// OSX13_FirefoxLatest_Incognito: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Firefox', browserVersion: 'latest-beta, ...firefoxIncognitoCapabilities },
OSX13_EdgeLatest: { platform: 'OS X', osVersion: 'Ventura', browserName: 'Edge', browserVersion: 'latest-beta', useHttps: true },
Android13_ChromeLatest: { deviceName: ['Google Pixel 7', 'Google Pixel 7 Pro', 'Google Pixel 6 Pro'], platform: 'Android', osVersion: '13.0', browserName: 'Chrome', browserVersion: 'latest-beta', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS11_Safari: { deviceName: ['iPhone 8 Plus', 'iPhone 6S', 'iPhone 8', 'iPhone 6'], platform: 'iOS', osVersion: '11', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS12_Safari: { deviceName: ['iPhone XS', 'iPhone 6S', 'iPhone 8 Plus', 'iPhone XR'], platform: 'iOS', osVersion: '12', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS13_Safari: { deviceName: ['iPhone 11 Pro', 'iPhone 8', 'iPhone XS', 'iPhone 11 Pro Max'], platform: 'iOS', osVersion: '13', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS14_Safari: { deviceName: ['iPhone 11', 'iPhone XS', 'iPhone 12 Pro', 'iPhone 12 mini'], platform: 'iOS', osVersion: '14', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS15_Safari: { deviceName: ['iPhone 13', 'iPhone 13 Mini', 'iPhone 11 Pro', 'iPhone 11'], platform: 'iOS', osVersion: '15', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent], },
iOS16_Safari: { deviceName: ['iPhone 14', 'iPhone 14 Pro Max', 'iPhone 14 Pro', 'iPhone 14 Plus'], platform: 'iOS', osVersion: '16', browserName: 'Safari', useHttps: true, flags: [BrowserFlags.MobileUserAgent],},
}

/* eslint-enable max-len */

function makeBuildNumber() {
return `No CI ${Math.floor(Math.random() * 1e10)}`
}

function setupLocal(config: Config) {
const ciSpecificFiles = ['resources/karma/karma_global_setup_retries.ts']
const ciEnabled = process.env.CI
const files = [...(ciEnabled ? ciSpecificFiles : []), 'src/**/*.ts', 'tests/**/*.ts', 'test-dist/botd.min.js']

config.set({
frameworks: ['jasmine', 'karma-typescript'],
files,
preprocessors: {
'**/*.ts': 'karma-typescript',
},
reporters: ['spec', 'summary'],
browsers: ['ChromeHeadless', 'FirefoxHeadless'],
concurrency: 3,

karmaTypescriptConfig: {
tsconfig: 'tsconfig.json',
compilerOptions: {
module: 'commonjs',
sourceMap: true,
},
include: files,
},

specReporter: {
suppressSummary: true,
suppressErrorSummary: true,
suppressPassed: true,
suppressSkipped: true,
},
})
}

function setupBrowserstack(config: Config) {
setupLocal(config)

const customLaunchers: Record<string, CustomLauncher> = {}
for (const [key, data] of Object.entries(browserstackBrowsers)) {
customLaunchers[key] = {
base: 'BrowserStack',
name: key.replace(/_/g, ' '),
...data,
}
}

config.set({
reporters: [...(config.reporters || []), 'BrowserStack'],
plugins: [karmaPlugin, 'karma-*'],
browsers: Object.keys(customLaunchers),
customLaunchers,
concurrency: 5,
failOnEmptyTestSuite: false,
retryLimit: 3,
captureTimeout: 15_000,
browserStack: {
project: 'BotD',
// A build number is required to group testing sessions in the BrowserStack UI.
// GitHub Actions will add a value for GITHUB_RUN_ID. More on the environment variables:
// https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables#default-environment-variables
build: process.env.GITHUB_RUN_ID || makeBuildNumber(),
// The timeout is reduced for testing sessions to not hold the BrowserStack queue long in case of problems.
idleTimeout: 20_000,
queueTimeout: 300_000,
},
})
setHttpsAndServerForKarma(config)
}

function setupBrowserStackBetaBuilds(config: Config) {
setupBrowserstack(config)

const customLaunchers: Record<string, CustomLauncher> = {}
for (const [key, data] of Object.entries(browserstackBrowsers)) {
if ('browserVersion' in data && data['browserVersion'].includes('beta')) {
customLaunchers[key] = {
base: 'BrowserStack',
name: key.replace(/_/g, ' '),
...data,
}
}
}

config.set({
browsers: Object.keys(customLaunchers),
customLaunchers,

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
browserStack: { ...config.browserStack!, project: 'Monitoring' },
})
}

/**
* Add `--preset local` or `--preset browserstack` to the Karma command to choose where to run the tests.
*/
module.exports = (config: Config) => {
switch (config.preset) {
case 'local':
return setupLocal(config)
case 'browserstack':
return setupBrowserstack(config)
case 'browserstack-beta':
return setupBrowserStackBetaBuilds(config)
default:
throw new Error('No --preset option is set or an unknown value is set')
}
}
})
11 changes: 1 addition & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,11 @@
"check:ssr": "node --require ./dist/botd.cjs.js --eval '' || (echo \"The distributive files can't be used with server side rendering. Make sure the code doesn't use browser API until an exported function is called.\" && exit 1)"
},
"devDependencies": {
"@fpjs-incubator/broyster": "^0.1.5",
"@fpjs-incubator/broyster": "^0.1.7",
"@rollup/plugin-json": "^5.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^10.0.1",
"@types/jasmine": "^3.5.14",
"@types/karma": "^6.3.3",
"@types/karma-spec-reporter": "^0.0.3",
"@types/karma-summary-reporter": "^3.1.0",
"@types/ua-parser-js": "^0.7.36",
"@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.40.1",
Expand All @@ -67,12 +64,6 @@
"eslint-plugin-prettier": "^3.3.1",
"html-webpack-plugin": "^5.5.0",
"karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1",
"karma-firefox-launcher": "^2.1.2",
"karma-jasmine": "^4.0.2",
"karma-spec-reporter": "^0.0.34",
"karma-summary-reporter": "^3.1.1",
"karma-typescript": "^5.5.3",
"license-webpack-plugin": "^4.0.2",
"prettier": "^2.2.1",
"promise-polyfill": "^8.2.0",
Expand Down
4 changes: 4 additions & 0 deletions playground/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
getMozAppearanceSupport,
isAndroid,
isDesktopWebKit,
isIPad,
} from '../src/utils/browser'
import { getBrowserVersion } from '../tests/utils'
import './style.css'

type DetectionResult =
Expand Down Expand Up @@ -40,10 +42,12 @@ const runDetection = async (): Promise<DetectionResult> => {
const debugData = {
browserEngineKind: getBrowserEngineKind(),
browserKind: getBrowserKind(),
browserVersion: getBrowserVersion(),
documentFocus: getDocumentFocus(),
mozAppearanceSupport: getMozAppearanceSupport(),
isAndroid: isAndroid(),
isDesktopWebKit: isDesktopWebKit(),
isIPad: isIPad(),
}

return {
Expand Down
2 changes: 2 additions & 0 deletions playground/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ const config: Configuration = {

performance: {
hints: isDev ? false : 'error',
maxAssetSize: Infinity,
maxEntrypointSize: Infinity,
},
}

Expand Down
3 changes: 0 additions & 3 deletions resources/karma/karma_global_setup_retries.ts

This file was deleted.

Loading

0 comments on commit 19dddc0

Please sign in to comment.