1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-04 18:29:17 +02:00

Merge branch 'master' into group-feature

This commit is contained in:
Denis barbaron 2020-01-24 10:47:35 +01:00
commit 1dd9f45f06
26 changed files with 803 additions and 47 deletions

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
8.16.1

1
.tool-versions Normal file
View file

@ -0,0 +1 @@
nodejs 8.16.1

View file

@ -15,13 +15,14 @@ addons:
- yasm
env:
matrix:
- NODE_VERSION=6
- NODE_VERSION=8.16.1
matrix:
allow_failures:
- os: osx
fast_finish: true
before_install:
- rm -rf ~/.nvm && git clone --depth 1 https://github.com/creationix/nvm.git ~/.nvm
- rm .nvmrc
- source ~/.nvm/nvm.sh
- nvm install $NODE_VERSION
- node --version

View file

@ -23,9 +23,9 @@ Thank you to all our sponsors! (please ask your company to also support this ope
#### Gold Sponsor
[<img src="doc/sponsors/headspin-wordmark-orange.png?raw=true" alt="HeadSpin" width="400">](https://headspin.io/)
[<img src="doc/sponsors/headspin-wordmark-orange.png?raw=true" alt="HeadSpin" width="400">](https://ui.headspin.io/register?referral=start-testing-hs)
> [HeadSpin](https://headspin.io/) provides secure and scalable STF for iOS integrated with Appium/XCTest/Selenium/Espresso, High speed interaction Audio/Video/Game testing and AI based Root cause analysis for Performance Management. It's free to start using HeadSpin in 150+ locations worldwide! [Try it out for free.](https://ui-dev.headspin.io/signup/23feaa95fec34c49a1da309380807795)
> [HeadSpin](https://headspin.io/) provides secure and scalable STF for iOS integrated with Appium/XCTest/Selenium/Espresso, High speed interaction Audio/Video/Game testing and AI based Root cause analysis for Performance Management. It's free to start using HeadSpin in 150+ locations worldwide! [Try it out for free.](https://ui.headspin.io/register?referral=start-testing-hs)
HeadSpin offers a generous monthly contribution towards STF development.
@ -114,7 +114,7 @@ As the product has evolved from an internal tool running in our internal network
## Requirements
* [Node.js](https://nodejs.org/) >= 6.9 (latest stable version preferred)
* [Node.js](https://nodejs.org/) 8.x **required** (some dependencies don't support newer versions)
* [ADB](http://developer.android.com/tools/help/adb.html) properly set up
* [RethinkDB](http://rethinkdb.com/) >= 2.2
* [GraphicsMagick](http://www.graphicsmagick.org/) (for resizing screenshots)

View file

@ -5,20 +5,49 @@
## E2E Frontend
### On first run
## On first run
- `gulp webdriver-update`
### Chrome Local STF
- Connect a device
- Run stf
- `gulp protractor`
### Multiple Browsers Local STF with a specific suite
## Protractor&Jasmine - Local STF tests
---
#### Preconditions
Test configuration point to Google Chrome browser. Test works on Google Chrome v.77.0.3865.75 together with chromedriver with ver. 77.0.3865.40.
---
- Connect a device or start android emulator
- Run RethinkDb
```
rethinkdb
```
- Run stf
```
./bin/stf local
```
Wait till STF will be fully functional and devices will be discovered
- Run tests
```
gulp protractor
```
---
#### Info
Test results can be found in:
test-results/reports-protractor/dashboardReport-protractor/index.html
---
===
## Multiple Browsers Local STF with a specific suite
- Connect a device
- Run stf
- `gulp protractor --multi --suite devices`
### Chrome Remote STF
## Chrome Remote STF
- `export STF_URL='http://stf-url/#!/'`
- `export STF_USERNAME='user'`
- `export STF_PASSWORD='pass'`

View file

@ -1210,6 +1210,7 @@ dbapi.saveDeviceIdentity = function(serial, identity) {
, product: identity.product
, cpuPlatform: identity.cpuPlatform
, openGLESVersion: identity.openGLESVersion
, marketName: identity.marketName
}))
}

View file

@ -44,6 +44,7 @@ module.exports = syrup.serial()
, identity.product
, identity.cpuPlatform
, identity.openGLESVersion
, identity.marketName
))
])
})

View file

@ -62,7 +62,7 @@ module.exports = syrup.serial()
}
function removeResource(res) {
return adb.shell(options.serial, ['rm', res.dest])
return adb.shell(options.serial, ['rm', '-f', res.dest])
.timeout(10000)
.then(function(out) {
return streamutil.readAll(out)

View file

@ -36,7 +36,7 @@ module.exports = syrup.serial()
}
function removeResource(res) {
return adb.shell(options.serial, ['rm', res.dest])
return adb.shell(options.serial, ['rm', '-f', res.dest])
.timeout(10000)
.then(function(out) {
return streamutil.readAll(out)

View file

@ -35,7 +35,7 @@ module.exports = syrup.serial()
}
function removeResource(res) {
return adb.shell(options.serial, ['rm', res.dest])
return adb.shell(options.serial, ['rm', '-f', res.dest])
.timeout(10000)
.then(function(out) {
return streamutil.readAll(out)

View file

@ -37,7 +37,7 @@ module.exports = function(options) {
})
.catch(function(err) {
log.error('Unable to read manifest of "%s"', req.params.id, err.stack)
res.status(500)
res.status(200)
.json({
success: false
})

View file

@ -2,6 +2,7 @@ var util = require('util')
var split = require('split')
var Promise = require('bluebird')
var androidDeviceList = require('android-device-list')
var devutil = module.exports = Object.create(null)
@ -135,6 +136,7 @@ devutil.makeIdentity = function(serial, properties) {
var product = properties['ro.product.name']
var cpuPlatform = properties['ro.board.platform']
var openGLESVersion = properties['ro.opengles.version']
var marketName = properties['ro.product.device']
openGLESVersion = parseInt(openGLESVersion, 10)
if (isNaN(openGLESVersion)) {
@ -157,6 +159,13 @@ devutil.makeIdentity = function(serial, properties) {
model = model.substr(manufacturer.length)
}
if (marketName) {
var devices = androidDeviceList.getDevicesByDeviceId(marketName)
if (devices.length > 0) {
marketName = devices[0].name
}
}
// Clean up remaining model name
// model = model.replace(/[_ ]/g, '')
return {
@ -171,5 +180,6 @@ devutil.makeIdentity = function(serial, properties) {
, product: product
, cpuPlatform: cpuPlatform
, openGLESVersion: openGLESVersion
, marketName: marketName
}
}

View file

@ -36,6 +36,8 @@ module.exports.readAll = function(stream) {
stream.on('readable', readableListener)
stream.on('end', endListener)
readableListener()
return resolver.promise.finally(function() {
stream.removeListener('error', errorListener)
stream.removeListener('readable', readableListener)

View file

@ -408,6 +408,7 @@ message DeviceIdentityMessage {
optional string product = 12;
optional string cpuPlatform = 13;
optional string openGLESVersion = 14;
optional string marketName = 15;
}
message DeviceProperty {

View file

@ -37,6 +37,7 @@
"adbkit": "^2.11.1",
"adbkit-apkreader": "^3.1.1",
"adbkit-monkey": "^1.0.1",
"android-device-list": "^1.2.1",
"aws-sdk": "^2.4.13",
"basic-auth": "^1.0.3",
"bluebird": "^2.10.1",
@ -99,7 +100,7 @@
},
"devDependencies": {
"async": "^2.0.1",
"bower": "^1.7.2",
"bower": "^1.8.8",
"chai": "^3.4.1",
"css-loader": "^0.23.1",
"del": "^2.0.1",
@ -108,6 +109,7 @@
"exports-loader": "^0.6.2",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"fs-extra": "^8.1.0",
"gulp": "^3.8.11",
"gulp-angular-gettext": "^2.1.0",
"gulp-eslint": "^3.0.1",
@ -120,13 +122,13 @@
"http-https": "^1.0.0",
"imports-loader": "^0.6.5",
"jasmine-core": "^2.4.1",
"jasmine-reporters": "^2.1.1",
"jasmine-reporters": "^2.3.2",
"json-loader": "^0.5.4",
"karma": "^1.1.2",
"karma-chrome-launcher": "^1.0.1",
"karma": "^1.7.1",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.0.0",
"karma-ie-launcher": "^1.0.0",
"karma-jasmine": "^1.0.2",
"karma-jasmine": "^2.0.1",
"karma-junit-reporter": "^1.1.0",
"karma-opera-launcher": "^1.0.0",
"karma-phantomjs-launcher": "^1.0.0",
@ -138,8 +140,8 @@
"node-libs-browser": "^1.0.0",
"node-sass": "^3.4.2",
"phantomjs-prebuilt": "^2.1.11",
"protractor": "^4.0.3",
"protractor-html-screenshot-reporter": "0.0.21",
"protractor": "^5.4.1",
"protractor-html-reporter-2": "1.0.4",
"raw-loader": "^0.5.1",
"sass-loader": "^4.0.0",
"script-loader": "^0.7.0",

View file

@ -229,6 +229,12 @@ module.exports = function DeviceColumnService($filter, gettext, SettingsService,
return device.manufacturer || ''
}
})
, marketName: TextCell({
title: gettext('Market name')
, value: function(device) {
return device.marketName || ''
}
})
, sdk: NumberCell({
title: gettext('SDK')
, defaultOrder: 'desc'

View file

@ -59,6 +59,10 @@ module.exports = function DeviceListCtrl(
name: 'manufacturer'
, selected: false
}
, {
name: 'marketName'
, selected: false
}
, {
name: 'sdk'
, selected: false

View file

@ -1,10 +1,11 @@
describe('Control Page', function() {
var DeviceListPage = require('../devices')
var deviceListPage = new DeviceListPage()
var localhost = browser.baseUrl
var ControlPage = function() {
this.get = function() {
browser.get(protractor.getInstance().baseUrl + 'control')
browser.get(localhost + 'control')
}
this.kickDeviceButton = element.all(by.css('.kick-device')).first()
this.kickDevice = function() {
@ -26,8 +27,8 @@ describe('Control Page', function() {
browser.sleep(500)
browser.getLocationAbsUrl().then(function(newUrl) {
expect(newUrl).toMatch(protractor.getInstance().baseUrl + 'control')
browser.getCurrentUrl().then(function(newUrl) {
expect(newUrl).toContain(localhost + 'control/')
})
})
@ -135,10 +136,10 @@ describe('Control Page', function() {
it('should stop controlling an usable device', function() {
controlPage.kickDevice()
waitUrl(/devices/)
browser.wait(waitUrl(/devices/), 5000)
browser.getLocationAbsUrl().then(function(newUrl) {
expect(newUrl).toBe(protractor.getInstance().baseUrl + 'devices')
browser.getCurrentUrl().then(function(newUrl) {
expect(newUrl).toBe(localhost + 'devices')
})
})

View file

@ -6,8 +6,8 @@ describe('Device Page', function() {
it('should go to Devices List page', function() {
deviceListPage.get()
browser.getLocationAbsUrl().then(function(newUrl) {
expect(newUrl).toBe(protractor.getInstance().baseUrl + 'devices')
browser.getCurrentUrl().then(function(newUrl) {
expect(newUrl).toBe(browser.baseUrl + 'devices')
})
})

View file

@ -1,7 +1,7 @@
module.exports = function DeviceListPage() {
this.get = function() {
// TODO: Let's get rid off the login first
browser.get(protractor.getInstance().baseUrl + 'devices')
browser.get(browser.baseUrl + 'devices')
}
this.devices = element(by.model('tracker.devices'))
this.devicesByCss = element.all(by.css('ul.devices-icon-view > li'))

View file

@ -15,7 +15,7 @@ module.exports = function BrowserLogs(opts) {
}
browser.getCapabilities().then(function(cap) {
var browserName = ' ' + cap.caps_.browserName + ' log '
var browserName = ' ' + cap.browserName + ' log '
var browserStyled = chalk.bgBlue.white.bold(browserName) + ' '
browser.manage().logs().get('browser').then(function(browserLogs) {

View file

@ -1,5 +1,5 @@
module.exports = function LoginPage() {
this.login = protractor.getInstance().params.login
this.login = browser.params.login
this.get = function() {
return browser.get(this.login.url)
@ -17,17 +17,24 @@ module.exports = function LoginPage() {
this.setName = function(username) {
return this.username.sendKeys(username)
}
this.setEmail = function(email) {
return this.email.sendKeys(email)
}
this.setPassword = function(password) {
return this.password.sendKeys(password)
}
this.submit = function() {
return this.username.submit()
}
this.doLogin = function() {
var EC = protractor.ExpectedConditions
var timeout = 15000
this.get()
browser.wait(EC.presenceOf(element(by.css('[value="Log In"]'))), timeout)
this.setName(this.login.username)
if (this.login.method === 'ldap') {
this.setPassword(this.login.password)
@ -43,6 +50,7 @@ module.exports = function LoginPage() {
})
})
}
this.cleanUp = function() {
this.username = null
this.password = null

View file

@ -2,14 +2,20 @@ describe('Login Page', function() {
var LoginPage = require('./')
var loginPage = new LoginPage()
beforeEach(function() {
browser.executeScript('window.localStorage.clear();')
browser.executeScript('window.sessionStorage.clear();')
browser.driver.manage().deleteAllCookies()
})
it('should have an url to login', function() {
expect(loginPage.login.url).toMatch('http')
})
it('should login with method: "' + loginPage.login.method + '"', function() {
loginPage.doLogin().then(function() {
browser.getLocationAbsUrl().then(function(newUrl) {
expect(newUrl).toBe(protractor.getInstance().baseUrl + 'devices')
browser.getCurrentUrl().then(function(newUrl) {
expect(newUrl).toBe(browser.baseUrl + 'devices')
})
})
})

View file

@ -1,6 +1,5 @@
var config = require('./protractor.conf').config
//var LoginPage = require('./e2e/login')
//var HtmlReporter = require('protractor-html-screenshot-reporter')
//var WaitUrl = require('./e2e/helpers/wait-url')
config.chromeOnly = false

View file

@ -2,8 +2,12 @@
var LoginPage = require('./e2e/login')
var BrowserLogs = require('./e2e/helpers/browser-logs')
//var FailFast = require('./e2e/helpers/fail-fast')
var HtmlReporter = require('protractor-html-screenshot-reporter')
var jasmineReporters = require('jasmine-reporters')
var WaitUrl = require('./e2e/helpers/wait-url')
var HTMLReport = require('protractor-html-reporter-2')
var reportsDirectory = './test-results/reports-protractor'
var dashboardReportDirectory = reportsDirectory + '/dashboardReport'
module.exports.config = {
baseUrl: process.env.STF_URL || 'http://localhost:7100/#!/',
@ -17,7 +21,7 @@ module.exports.config = {
params: {
login: {
url: process.env.STF_LOGINURL || process.env.STF_URL ||
'http://localhost:7120',
'http://localhost:7100',
username: process.env.STF_USERNAME || 'test_user',
email: process.env.STF_EMAIL || 'test_user@login.local',
password: process.env.STF_PASSWORD,
@ -34,7 +38,7 @@ module.exports.config = {
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: ['--test-type'] // Prevent security warning bug in ChromeDriver
args: ['--test-type --no-sandbox'] // Prevent security warning bug in ChromeDriver
}
},
chromeOnly: true,
@ -45,16 +49,60 @@ module.exports.config = {
this.waitUrl = WaitUrl
jasmine.getEnv().addReporter(new HtmlReporter({
baseDirectory: './res/test/test_out/screenshots'
jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
consolidateAll: true,
savePath: reportsDirectory + '/xml',
filePrefix: 'xmlOutput'
}))
var fs = require('fs-extra')
if (!fs.existsSync(dashboardReportDirectory)) {
fs.mkdirs(dashboardReportDirectory)
}
jasmine.getEnv().addReporter({
specDone: function(result) {
if (result.status === 'failed') {
browser.getCapabilities().then(function(caps) {
var browserName = caps.get('browserName')
browser.takeScreenshot().then(function(png) {
var stream = fs.createWriteStream(dashboardReportDirectory + '/' +
browserName + '-' + result.fullName + '.png')
stream.write(new Buffer(png, 'base64'))
stream.end()
})
})
}
}
})
afterEach(function() {
BrowserLogs({expectNoLogs: true})
//FailFast()
})
},
onComplete: function() {
var browserName, browserVersion, platform, testConfig
var capsPromise = browser.getCapabilities()
capsPromise.then(function(caps) {
browserName = caps.get('browserName')
browserVersion = caps.get('version')
platform = caps.get('platform')
testConfig = {
reportTitle: 'Protractor Test Execution Report',
outputPath: dashboardReportDirectory,
outputFilename: 'index',
screenshotPath: './',
testBrowser: browserName,
browserVersion: browserVersion,
modifiedSuiteName: false,
screenshotsOnlyOnFailure: true,
testPlatform: platform
}
new HTMLReport().from(reportsDirectory + '/xml/xmlOutput.xml', testConfig)
})
}
}

647
yarn.lock

File diff suppressed because it is too large Load diff