Skip to content

Commit db9a320

Browse files
authored
New: Setup selenium functional UI framework (#158)
* Set up express app to generate annotator tokens and test browsers * Update travis.yml w/ saucelabs config * Allowing concurrent testing w/ file ids in env variables * Chore: Only run tests on cron jobs * Chore: Only run enabled functional-tests * Chore: Run tests using all files in functional-tests/tests/ folder * Chore: Send travis alert emails to preview-alerts@box.com
1 parent 7f62860 commit db9a320

File tree

16 files changed

+3200
-1243
lines changed

16 files changed

+3200
-1243
lines changed

.babelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"**/*-test.js",
2828
"build/**",
2929
"docs/**",
30-
"lib'**",
30+
"lib'**"
3131
]
3232
}],
3333
["babel-plugin-transform-require-ignore", { "extensions": [".scss"] }]

.eslintrc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
"assert": false,
2424
"fixture": false,
2525
"__": false,
26-
"Assert": false
26+
"Assert": false,
27+
"Feature": false,
28+
"Before": false,
29+
"BeforeSuite": false,
30+
"Scenario": false
2731
},
2832
"rules": {
2933
"quotes": [

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ src/i18n/json
1414
test/dev.html
1515
lib
1616
*~~bak
17+
functional-tests/output

.travis.yml

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,57 @@
1+
# @desktop @mobile @enabled
12
language: node_js
23
node_js:
3-
- "8"
4-
script: yarn run ci
4+
- '8'
55
cache: yarn
66
notifications:
7-
email: false
8-
9-
# safelist
10-
branches:
11-
only:
12-
- master
13-
- /^greenkeeper/.*$/
7+
email:
8+
recipients:
9+
- preview-alerts@box.com
10+
aliases:
11+
- &sauce-labs
12+
if: type = cron
13+
before_script:
14+
- yarn run setup-travis &
15+
- sleep 5
16+
addons:
17+
sauce_connect: true
18+
script: yarn run functional-tests-ci
19+
- &sauce-labs-mobile
20+
<<: *sauce-labs
21+
script: yarn run functional-tests-ci --grep @mobile --grep @enabled
22+
- &sauce-labs-desktop
23+
<<: *sauce-labs
24+
script: yarn run functional-tests-ci --grep @desktop --grep @enabled
25+
jobs:
26+
include:
27+
- script: yarn run ci
28+
# Mac Chrome
29+
- <<: *sauce-labs-desktop
30+
env: BROWSER_PLATFORM="macOS 10.13" BROWSER_NAME="chrome" FILE_ID="285567874839" FILE_VERSION_ID="300496591287"
31+
# Mac Safari
32+
- <<: *sauce-labs-desktop
33+
env: BROWSER_PLATFORM="macOS 10.13" BROWSER_NAME="safari" FILE_ID="285569765346" FILE_VERSION_ID="300498497346"
34+
script: yarn run functional-tests-ci
35+
# Mac Firefox
36+
- <<: *sauce-labs-desktop
37+
env: BROWSER_PLATFORM="macOS 10.13" BROWSER_NAME="firefox" FILE_ID="285568802145" FILE_VERSION_ID="300497533713"
38+
# Windows Edge
39+
- <<: *sauce-labs-desktop
40+
env: BROWSER_PLATFORM="Windows 10" BROWSER_NAME="MicrosoftEdge" FILE_ID="285567976309" FILE_VERSION_ID="300496707445"
41+
script: yarn run functional-tests-ci
42+
# Windows IE
43+
- <<: *sauce-labs-desktop
44+
env: BROWSER_PLATFORM="Windows 10" BROWSER_NAME="internet explorer" FILE_ID="285568624824" FILE_VERSION_ID="300497342136"
45+
script: yarn run functional-tests-ci
46+
# iPhone
47+
- <<: *sauce-labs-mobile
48+
env: BROWSER_PLATFORM="iOS" DEVICE_NAME="iPhone 6 Simulator" PLATFORM_VERSION="11.2" BROWSER_NAME="Safari" FILE_ID="285569765346" FILE_VERSION_ID="300498497346"
49+
# iPad
50+
- <<: *sauce-labs-mobile
51+
# Uses firefox file id
52+
env: BROWSER_PLATFORM="iOS" DEVICE_NAME="iPad Simulator" PLATFORM_VERSION="11.2" BROWSER_NAME="Safari" FILE_ID="285568802145" FILE_VERSION_ID="300497533713"
53+
# Android
54+
- <<: *sauce-labs-mobile
55+
# Uses chrome file id
56+
env: BROWSER_PLATFORM="Android" DEVICE_NAME="Android GoogleAPI Emulator" PLATFORM_VERSION="7.1" BROWSER_NAME="Chrome" FILE_ID="285567874839" FILE_VERSION_ID="300496591287"
57+
script: yarn run functional-tests-ci

build/webpack.selenium.config.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require('babel-polyfill');
2+
3+
const isRelease = process.env.NODE_ENV === 'production';
4+
const isDev = process.env.NODE_ENV === 'dev';
5+
6+
const path = require('path');
7+
const commonConfig = require('./webpack.common.config');
8+
const { UglifyJsPlugin } = require('webpack').optimize;
9+
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
10+
const { BannerPlugin } = require('webpack');
11+
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
12+
const license = require('./license');
13+
14+
/* eslint-disable key-spacing, require-jsdoc */
15+
const config = Object.assign(commonConfig(), {
16+
entry: {
17+
annotations: ['./src/BoxAnnotations.js']
18+
},
19+
output: {
20+
path: path.resolve('functional-tests/lib'),
21+
filename: '[Name].js'
22+
}
23+
});
24+
25+
config.devtool = 'inline-source-map';
26+
27+
module.exports = config;

codecept.conf.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const {
2+
SAUCE_USERNAME,
3+
SAUCE_ACCESS_KEY,
4+
TRAVIS_JOB_NUMBER,
5+
CI,
6+
BROWSER_NAME = 'chrome',
7+
BROWSER_VERSION = 'latest',
8+
BROWSER_PLATFORM,
9+
PLATFORM_VERSION,
10+
DEVICE_NAME,
11+
DEFAULT_WAIT_TIME = 9000
12+
} = process.env;
13+
const MOBILE_PLATFORMS = ['iOS', 'Android'];
14+
15+
// Local selenium config
16+
const commonConfigObj = {
17+
browser: BROWSER_NAME,
18+
url: 'http://localhost:8080',
19+
restart: true,
20+
waitForTimeout: DEFAULT_WAIT_TIME
21+
};
22+
23+
const helperObj = {};
24+
const isLocalBuild = typeof SAUCE_USERNAME === 'undefined';
25+
26+
if (isLocalBuild) {
27+
helperObj.WebDriverIO = commonConfigObj;
28+
} else {
29+
// Common saucelab config
30+
const sauceObj = {
31+
host: 'ondemand.saucelabs.com',
32+
port: 80,
33+
user: SAUCE_USERNAME,
34+
key: SAUCE_ACCESS_KEY,
35+
desiredCapabilities: {
36+
name: CI ? 'Travis cron' : require('os').userInfo().username, // eslint-disable-line global-require
37+
build: TRAVIS_JOB_NUMBER,
38+
'tunnel-identifier': TRAVIS_JOB_NUMBER,
39+
browserName: BROWSER_NAME,
40+
platform: BROWSER_PLATFORM
41+
}
42+
};
43+
44+
const mixedInSauceObj = Object.assign({}, commonConfigObj, sauceObj);
45+
if (MOBILE_PLATFORMS.indexOf(BROWSER_PLATFORM) === -1) {
46+
// webdriver (desktop)
47+
Object.assign(sauceObj.desiredCapabilities, {
48+
version: BROWSER_VERSION
49+
});
50+
helperObj.WebDriverIO = mixedInSauceObj;
51+
} else {
52+
// appium (mobile)
53+
Object.assign(sauceObj.desiredCapabilities, {
54+
platformVersion: PLATFORM_VERSION,
55+
deviceName: DEVICE_NAME,
56+
deviceOrientation: 'portrait',
57+
appiumVersion: '1.7.2',
58+
platformName: BROWSER_PLATFORM
59+
});
60+
helperObj.Appium = mixedInSauceObj;
61+
}
62+
}
63+
64+
exports.config = {
65+
tests: './functional-tests/tests/*.js',
66+
timeout: DEFAULT_WAIT_TIME,
67+
output: './functional-tests/output',
68+
helpers: helperObj,
69+
include: {},
70+
bootstrap: './functional-tests/helpers/cleanup.js',
71+
teardown: './functional-tests/helpers/cleanup.js',
72+
mocha: {},
73+
name: 'box-annotations',
74+
hooks: isLocalBuild ? [] : ['./functional-tests/helpers/eventHooks.js']
75+
};

functional-tests/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Running tests locally
2+
3+
## SauceLabs
4+
1) Download the [saucelabs proxy](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy)
5+
2) Run ```yarn functional-tests``` to start a local server on localhost:8000
6+
3) Run the proxy ```./bin/sc -u SAUCELABS_USER_NAME -k SAUCELABS_ACCESS_KEY -N -i test``` to allow saucelabs to access your localhost
7+
4) Run the tests
8+
```SAUCE_USERNAME=SAUCELABS_USER_NAME SAUCE_ACCESS_KEY=SAUCELABS_ACCESS_KEY TRAVIS_JOB_NUMBER=TUNNEL_ID FILE_ID="285568802145" FILE_VERSION_ID="300497533713" node ./node_modules/codeceptjs/bin/codecept.js run --verbose``` where SAUCE_USERNAME, SAUCELABS_ACCESS_KEY can be found in saucelabs website. TUNNEL_ID is a unique identifier such as your username.
9+
10+
## Selenium (without SauceLabs)
11+
1) Install selenium-standalone `npm install selenium-standalone@latest -g`
12+
2) Install Selenium drivers `selenium-standalone install`
13+
3) Start Selenium `selenium-standalone start`
14+
4) In a separate terminal, build Preview `yarn run build`
15+
5) Run functional tests `FILE_ID="285568802145" FILE_VERSION_ID="300497533713" yarn run functional-tests`

functional-tests/app.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* eslint-disable func-names */
2+
/* eslint-disable prefer-arrow-callback */
3+
/* eslint-disable require-jsdoc */
4+
/* eslint-disable no-console */
5+
const express = require('express');
6+
const path = require('path');
7+
const BoxSDK = require('box-node-sdk');
8+
9+
const {
10+
FILE_ID,
11+
ACCESS_TOKEN,
12+
CLIENT_ID
13+
} = process.env;
14+
15+
const app = express();
16+
const server = app.listen(8080, () => console.log('Example app listening on port 8080!'));
17+
18+
// Set up SDK & client
19+
const sdk = new BoxSDK({
20+
clientID: CLIENT_ID,
21+
clientSecret: 'NUH UH'
22+
});
23+
const client = sdk.getBasicClient(ACCESS_TOKEN);
24+
25+
app.use(express.static(path.join(__dirname, 'lib')));
26+
app.set('view engine', 'pug');
27+
app.set('views', path.join(__dirname, 'views'));
28+
29+
// this function is called when you want the server to die gracefully
30+
// i.e. wait for existing connections
31+
function gracefulShutdown() {
32+
console.log('Received kill signal, shutting down gracefully.');
33+
server.close(function() {
34+
console.log('Closed out remaining connections.');
35+
process.exit();
36+
});
37+
38+
// if after
39+
setTimeout(function() {
40+
console.error('Could not close connections in time, forcefully shutting down');
41+
process.exit();
42+
}, 10*1000);
43+
}
44+
45+
function errorCallback(err) {
46+
console.log(err.response.body);
47+
gracefulShutdown();
48+
}
49+
50+
// viewed at http://localhost:8080
51+
app.get('/', function(req, res) {
52+
const url = `https://api.box.com/2.0/files/${FILE_ID}`;
53+
const options = {
54+
actor: {
55+
id: '3504101558',
56+
name: 'Kanye West'
57+
}
58+
};
59+
60+
client.exchangeToken(['item_preview'], url, options)
61+
.then((tokenInfo) => {
62+
// tokenInfo.accessToken contains the new annotator token
63+
res.render('index', { token: tokenInfo.accessToken, file_id: FILE_ID });
64+
})
65+
.catch(errorCallback);
66+
});
67+
68+
// listen for TERM signal .e.g. kill
69+
process.on ('SIGTERM', gracefulShutdown);
70+
71+
// listen for INT signal e.g. Ctrl-C
72+
process.on ('SIGINT', gracefulShutdown);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* eslint-disable func-names, prefer-arrow-callback, require-jsdoc, no-console */
2+
const BoxSDK = require('box-node-sdk');
3+
4+
const {
5+
FILE_ID,
6+
FILE_VERSION_ID,
7+
ACCESS_TOKEN,
8+
CLIENT_ID
9+
} = process.env;
10+
11+
const sdk = new BoxSDK({
12+
clientID: CLIENT_ID,
13+
clientSecret: 'NUH UH'
14+
});
15+
const client = sdk.getBasicClient(ACCESS_TOKEN);
16+
17+
function deleteAnnotation(annotation) {
18+
const { id } = annotation;
19+
client.del(`/annotations/${id}`, {}, function(err) {
20+
if (err) {
21+
// handle error
22+
throw err;
23+
}
24+
console.log(`Annotation ID ${id} was deleted`);
25+
});
26+
}
27+
28+
module.exports = function() {
29+
client.get(`/files/${FILE_ID}/annotations?version=${FILE_VERSION_ID}`, {}, function(err, response) {
30+
if (err) {
31+
// handle error
32+
throw err;
33+
}
34+
35+
const { entries } = response.body;
36+
if (!entries) {
37+
console.log('File does not have any existing annotations');
38+
return;
39+
}
40+
41+
console.log(`Deleting ${entries.length} annotations`);
42+
entries.forEach(deleteAnnotation);
43+
});
44+
}

0 commit comments

Comments
 (0)