Skip to content

Commit

Permalink
Add browser destination tests with saucelabs (segmentio#994)
Browse files Browse the repository at this point in the history
  • Loading branch information
silesky authored Jan 31, 2023
1 parent 2d728ca commit 7930d92
Show file tree
Hide file tree
Showing 27 changed files with 2,853 additions and 115 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ module.exports = {
rules: {
'@typescript-eslint/no-var-requires': 'off'
}
},
{
files: ['packages/browser-destinations-integration-tests/**/*.ts'],
rules: {
'@typescript-eslint/no-unsafe-call': 'off'
}
}
]
}
53 changes: 51 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,56 @@ jobs:
yarn subscriptions size
fi
browser-tests:
browser-tests-destination:
env:
SAUCE_USERNAME: ${{secrets.SAUCE_USERNAME}}
SAUCE_ACCESS_KEY: ${{secrets.SAUCE_ACCESS_KEY}}

runs-on: ubuntu-20.04

timeout-minutes: 20

strategy:
matrix:
node-version: [14.x]

steps:
- uses: actions/checkout@master

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
run: yarn install --frozen-lockfile
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Build
run: NODE_ENV=production yarn lerna run build --scope=@segment/browser-destinations --include-dependencies --stream

- name: Run Saucelabs Tests
working-directory: packages/browser-destinations-integration-tests
shell: bash
run: |
yarn start-destination-server &
yarn test:sauce
browser-tests-core:
runs-on: ubuntu-20.04

timeout-minutes: 10
Expand Down Expand Up @@ -107,7 +156,7 @@ jobs:
run: npx playwright install-deps

- name: Build
run: NODE_ENV=production yarn build
run: NODE_ENV=production yarn lerna run build --scope=@segment/actions-core --include-dependencies --stream

- name: Browser Test
run: yarn test-browser
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
"test-browser": "lerna run build:karma --stream && karma start",
"typecheck": "lerna run typecheck --stream",
"alpha": "lerna publish --canary --preid $(git branch --show-current) --include-merged-tags",
"prepare": "husky install"
"prepare": "husky install",
"clean": "sh scripts/clean.sh"
},
"devDependencies": {
"@peculiar/webcrypto": "^1.2.3",
"@types/chance": "^1.1.3",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.16",
"@types/jest": "^27.0.0",
"@types/ws": "^8.5.1",
"@typescript-eslint/eslint-plugin": "^4.14.0",
Expand All @@ -42,6 +44,7 @@
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-lodash": "^7.3.0",
"express": "^4.18.2",
"husky": "^7.0.0",
"jest": "^27.3.1",
"jest-browser-globals": "^25.1.0-beta",
Expand Down
2 changes: 1 addition & 1 deletion packages/actions-shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"@segment/destination-subscriptions": ["../destination-subscriptions/src"]
}
},
"exclude": [],
"exclude": ["dist"],
"include": ["src", "test"]
}
1 change: 1 addition & 0 deletions packages/browser-destinations-integration-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
driver-logs
33 changes: 33 additions & 0 deletions packages/browser-destinations-integration-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Browser Destinations Integration Tests

These are for tests that target the final, webpacked output of browser-destinations, and can ideally be run in multiple browsers via a device farm like Sauce Labs.

These tests are meant to run in real browsers and issues that might be missed in the package unit tests, which run in a node environment.

Browser targets:

- latest verison of chrome
- latest version of safari
- latest version of firefox
- iOS 13 (older version of safari)

## Running tests

1. Build dependencies and start local server

```sh
yarn browser-destinations:build &&
yarn start-destination-server
```

2. Run tests locally

```sh
yarn run test:local
```

3. Run tests on Sauce Labs

```sh
SAUCE_USERNAME=??? SAUCE_ACCESS_KEY=??? yarn run test:sauce
```
27 changes: 27 additions & 0 deletions packages/browser-destinations-integration-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@segment/browser-destinations-integration-tests",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/segmentio/action-destinations",
"directory": "packages/browser-destinations-integration-tests"
},
"private": true,
"scripts": {
"test:sauce": "wdio wdio.conf.sauce.ts",
"test:local": "wdio wdio.conf.local.ts",
"start-destination-server": "yarn ts-node src/server/start-destination-server.ts",
"browser-destinations:build": "NODE_ENV=production yarn lerna run build --scope=@segment/browser-destinations --include-dependencies --stream"
},
"dependencies": {},
"devDependencies": {
"@wdio/cli": "^7.26.0",
"@wdio/local-runner": "^7.26.0",
"@wdio/mocha-framework": "^7.26.0",
"@wdio/sauce-service": "^7.26.0",
"@wdio/spec-reporter": "^7.26.0",
"@wdio/types": "7",
"expect": "^29.4.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Page {
async loadDestination(destination: string): Promise<void> {
await browser.url(`${browser.options.baseUrl}?destination=${destination}`)

await browser.waitUntil(() => browser.execute(() => document.readyState === 'complete'), {
timeout: 10000
})
}
}

export default new Page()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const config = {
get destinationTestServerPort(): number {
return 5555
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { startServer } from './server'
import { listDestinations, DESTINATIONS_DIST_WEB } from './utils'
import express from 'express'
import path from 'path'

const htmlWithScript = (src: string): string => `
<html>
<head>
<script src="${src}" type="text/javascript"></script>
</head>
<body>
Script found: "${src}"
</body>
</html>
`

export const startDestinationServer = (...args: Parameters<typeof startServer>): ReturnType<typeof startServer> => {
const destinations = listDestinations()
return startServer(...args).then(([app, server]) => {
app.use('/js', express.static(path.join(DESTINATIONS_DIST_WEB)))
app.get('/', (req, res) => {
console.log('req!', req.url)
const { destination } = req.query
if (!destination) {
return res.status(400).send('No destination param passed.')
}
const foundDestination = destinations.find((d) => d.dirPath === destination)
if (!foundDestination) {
return res.status(404).send('Cannot find destination.')
}
res.send(htmlWithScript(path.join('js', foundDestination.dirPath, foundDestination.fileName)))
})
app.get('/destinations', (_, res) => {
const dirNames = destinations.map((d) => d.dirPath)
res.json(dirNames)
})

return [app, server]
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import express from 'express'
import { Server } from 'http'

const onExit = (server: Server) => {
return (): void => {
console.log('closing server...')
server.close(() => {
console.log('closed gracefully!')
process.exit()
})
setTimeout(() => {
console.log('Force closing!')
process.exit(1)
}, 400)
}
}
export const startServer = (port: string | number): Promise<[express.Application, Server]> => {
if (!port) {
throw new Error('please pass a PORT')
}
return new Promise((resolve) => {
const app = express()
const server = app.listen(port, () => {
console.log(`Listening on http://localhost:${port} in ${app.get('env')}`)
resolve([app, server])
})
process.on('SIGINT', onExit(server))
process.on('SIGTERM', onExit(server))
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config } from './config'
import { startDestinationServer } from './destination-server'

void startDestinationServer(config.destinationTestServerPort).catch(console.error)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { readdirSync } from 'fs'
import path from 'path'

const ls = (dirPath: string) =>
readdirSync(dirPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)

export const DESTINATIONS_DIST_WEB = path.join(__dirname, '../../../browser-destinations', 'dist', 'web')

export const listDestinations = () =>
ls(DESTINATIONS_DIST_WEB).map((dirPath) => {
const destinationDirPath = path.join(DESTINATIONS_DIST_WEB, dirPath)
const fileName = readdirSync(destinationDirPath).find((el) => el.endsWith('js'))
if (!fileName) throw new Error('Invariant: no .js file found.')
return {
dirPath: dirPath,
fileName,
filePath: path.join(destinationDirPath, fileName)
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import page from '../pageobjects/page'
import { expect } from 'expect'
import { listDestinations } from '../server/utils'

const allDestinations = listDestinations().map((el) => el.dirPath)

describe('Bundles are capable of being parsed and loaded without errors', () => {
for (const destination of allDestinations) {
it(destination, async () => {
await page.loadDestination(destination)

// written as a string so not transpiled -- using old JS to allow testing in old browsers.
// the "return" is important for this to work on saucelabs.
const code = `return (function() {
for (var key in window) {
if (key.indexOf('Destination') !== -1 && key.indexOf('webpack') === -1) {
return typeof window[key]
}
}
})()`
const destinationGlobalType = await browser.execute(code)
expect(destinationGlobalType).toBe('function')
})
}
})
16 changes: 16 additions & 0 deletions packages/browser-destinations-integration-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"moduleResolution": "node",
"module": "CommonJS",
"target": "es6",
"esModuleInterop": true,
"types": ["node", "webdriverio/async", "@wdio/mocha-framework", "expect-webdriverio", "@wdio/sauce-service"]
},
"ts-node": {
"transpileOnly": true,
"files": true,
"compilerOptions": {
"module": "commonjs" // get rid of "Cannot use import statement outside a module" for local scripts
}
}
}
Loading

0 comments on commit 7930d92

Please sign in to comment.