Merge pull request #3805 from MetaMask/ci-screens

CI - capture screenshots
feature/default_network_editable
kumavis 7 years ago committed by GitHub
commit 40c3edb754
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 78
      .circleci/config.yml
  2. 52
      development/metamaskbot-build-announce.js
  3. 3443
      package-lock.json
  4. 13
      package.json
  5. 6
      test/e2e/metamask.spec.js
  6. 18
      test/screens/func.js
  7. 230
      test/screens/new-ui.js
  8. 5
      ui/app/actions.js
  9. 8
      ui/app/components/dropdowns/network-dropdown.js
  10. 10
      ui/index.js

@ -17,8 +17,17 @@ workflows:
- prep-deps-npm
- test-e2e:
requires:
- prep-deps-npm
- prep-build
- job-screens:
requires:
- prep-deps-npm
- prep-build
- job-announce:
requires:
- prep-deps-npm
- prep-build
- job-screens
- test-unit:
requires:
- prep-deps-npm
@ -45,6 +54,7 @@ workflows:
- test-lint
- test-unit
- test-e2e
- job-screens
- test-integration-mascara-chrome
- test-integration-mascara-firefox
- test-integration-flat-chrome
@ -98,32 +108,7 @@ jobs:
key: build-cache-{{ .Revision }}
paths:
- dist
- store_artifacts:
path: dist/mascara
destination: builds/mascara
- store_artifacts:
path: builds
destination: builds
- run:
name: build:announce
command: |
CIRCLE_PR_NUMBER="${CIRCLE_PR_NUMBER:-${CIRCLE_PULL_REQUEST##*/}}"
SHORT_SHA1=$(echo $CIRCLE_SHA1 | cut -c 1-7)
BUILD_LINK_BASE="https://$CIRCLE_BUILD_NUM-42009758-gh.circle-artifacts.com/0/builds"
VERSION=$(node -p 'require("./dist/chrome/manifest.json").version')
MASCARA="$BUILD_LINK_BASE/mascara/home.html"
CHROME="$BUILD_LINK_BASE/metamask-chrome-$VERSION.zip"
FIREFOX="$BUILD_LINK_BASE/metamask-firefox-$VERSION.zip"
OPERA="$BUILD_LINK_BASE/metamask-opera-$VERSION.zip"
EDGE="$BUILD_LINK_BASE/metamask-edge-$VERSION.zip"
COMMENT_MAIN="Builds ready [$SHORT_SHA1]: [mascara][mascara], [chrome][chrome], [firefox][firefox], [edge][edge], [opera][opera]"
COMMENT_LINKS="[mascara]:$MASCARA\n[chrome]:$CHROME\n[firefox]:$FIREFOX\n[opera]:$OPERA\n[edge]:$EDGE\n"
COMMENT_BODY="$COMMENT_MAIN\n\n$COMMENT_LINKS"
JSON_PAYLOAD="{\"body\":\"$COMMENT_BODY\"}"
POST_COMMENT_URI="https://api.github.com/repos/metamask/metamask-extension/issues/$CIRCLE_PR_NUMBER/comments"
echo "Announcement:\n$COMMENT_BODY"
echo "Posting to $POST_COMMENT_URI"
curl -d "$JSON_PAYLOAD" -H "Authorization: token $GITHUB_COMMENT_TOKEN" $POST_COMMENT_URI
- builds
prep-scss:
docker:
@ -171,6 +156,47 @@ jobs:
path: test-artifacts
destination: test-artifacts
job-screens:
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- restore_cache:
key: build-cache-{{ .Revision }}
- run:
name: Test
command: npm run test:screens
- save_cache:
key: job-screens-{{ .Revision }}
paths:
- test-artifacts
job-announce:
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- restore_cache:
key: build-cache-{{ .Revision }}
- restore_cache:
key: job-screens-{{ .Revision }}
- store_artifacts:
path: dist/mascara
destination: builds/mascara
- store_artifacts:
path: builds
destination: builds
- store_artifacts:
path: test-artifacts
destination: test-artifacts
- run:
name: build:announce
command: ./development/metamaskbot-build-announce.js
test-unit:
docker:
- image: circleci/node:8-browsers

@ -0,0 +1,52 @@
#!/usr/bin/env node
const request = require('request-promise')
const { version } = require('../dist/chrome/manifest.json')
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN
console.log('GITHUB_COMMENT_TOKEN', GITHUB_COMMENT_TOKEN)
const CIRCLE_PULL_REQUEST = process.env.CIRCLE_PULL_REQUEST
console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST)
const CIRCLE_SHA1 = process.env.CIRCLE_SHA1
console.log('CIRCLE_SHA1', CIRCLE_SHA1)
const CIRCLE_BUILD_NUM = process.env.CIRCLE_BUILD_NUM
console.log('CIRCLE_BUILD_NUM', CIRCLE_BUILD_NUM)
const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop()
const SHORT_SHA1 = CIRCLE_SHA1.slice(0,7)
const BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0`
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html`
const CHROME = `${BUILD_LINK_BASE}/builds/metamask-chrome-${version}.zip`
const FIREFOX = `${BUILD_LINK_BASE}/builds/metamask-firefox-${version}.zip`
const EDGE = `${BUILD_LINK_BASE}/builds/metamask-edge-${version}.zip`
const OPERA = `${BUILD_LINK_BASE}/builds/metamask-opera-${version}.zip`
const WALKTHROUGH = `${BUILD_LINK_BASE}/test-artifacts/screens/walkthrough%20%28en%29.gif`
const commentBody = `
<details>
<summary>
Builds ready [${SHORT_SHA1}]:
<a href="${MASCARA}">mascara</a>,
<a href="${CHROME}">chrome</a>,
<a href="${FIREFOX}">firefox</a>,
<a href="${EDGE}">edge</a>,
<a href="${OPERA}">opera</a>
</summary>
<image src="${WALKTHROUGH}">
</details>
`
const JSON_PAYLOAD = JSON.stringify({ body: commentBody })
const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${CIRCLE_PR_NUMBER}/comments`
console.log(`Announcement:\n${commentBody}`)
console.log(`Posting to: ${POST_COMMENT_URI}`)
request({
method: 'POST',
uri: POST_COMMENT_URI,
body: JSON_PAYLOAD,
headers: {
'User-Agent': 'metamaskbot',
'Authorization': `token ${GITHUB_COMMENT_TOKEN}`,
},
})

3443
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -12,7 +12,10 @@
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
"test:integration:build": "gulp build:scss",
"test:e2e": "METAMASK_ENV=test mocha test/e2e/metamask.spec --recursive",
"test:e2e": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run'",
"test:e2e:run": "mocha test/e2e/metamask.spec --recursive",
"test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'",
"test:screens:run": "node test/screens/new-ui.js",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
@ -27,6 +30,7 @@
"test:mascara:build:locales": "mkdirp dist/chrome && cp -R app/_locales dist/chrome/_locales",
"test:mascara:build:background": "browserify mascara/src/background.js -o dist/mascara/background.js",
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
"ganache:start": "ganache-cli -m 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'",
"sentry": "export RELEASE=`cat app/manifest.json| jq -r .version` && npm run sentry:release && npm run sentry:upload",
"sentry:release": "npm run sentry:release:new && npm run sentry:release:clean",
"sentry:release:new": "sentry-cli releases --org 'metamask' --project 'metamask' new $RELEASE",
@ -140,7 +144,6 @@
"number-to-bn": "^1.7.0",
"obj-multiplex": "^1.0.0",
"obs-store": "^3.0.0",
"once": "^1.3.3",
"percentile": "^1.2.0",
"pify": "^3.0.0",
"ping-pong-stream": "^1.0.0",
@ -219,6 +222,8 @@
"eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^1.2.7",
"fs-promise": "^2.0.3",
"ganache-cli": "^6.1.0",
"gifencoder": "^1.1.0",
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
"gulp-babel": "^7.0.0",
"gulp-eslint": "^4.0.0",
@ -233,6 +238,7 @@
"gulp-util": "^3.0.7",
"gulp-watch": "^5.0.0",
"gulp-zip": "^4.0.0",
"image-size": "^0.6.2",
"isomorphic-fetch": "^2.2.1",
"jsdom": "^11.2.0",
"jsdom-global": "^3.0.2",
@ -251,6 +257,7 @@
"node-sass": "^4.7.2",
"nyc": "^11.0.3",
"open": "0.0.5",
"png-file-stream": "^1.0.0",
"prompt": "^1.0.0",
"qs": "^6.2.0",
"qunitjs": "^2.4.1",
@ -258,7 +265,9 @@
"react-test-renderer": "^15.6.2",
"react-testutils-additions": "^15.2.0",
"redux-test-utils": "^0.2.2",
"rimraf": "^2.6.2",
"selenium-webdriver": "^3.5.0",
"shell-parallel": "^1.0.3",
"sinon": "^5.0.0",
"stylelint-config-standard": "^18.2.0",
"tape": "^4.5.1",

@ -38,6 +38,8 @@ describe('Metamask popup page', function () {
const tabs = await driver.getAllWindowHandles()
await driver.switchTo().window(tabs[0])
await delay(300)
await setProviderType('localhost')
await delay(300)
})
it('should match title', async () => {
@ -124,6 +126,10 @@ describe('Metamask popup page', function () {
})
})
async function setProviderType(type) {
await driver.executeScript('window.metamask.setProviderType(arguments[0])', type)
}
async function verboseReportOnFailure(test) {
const artifactDir = `./test-artifacts/${test.title}`
const filepathBase = `${artifactDir}/test-failure`

@ -0,0 +1,18 @@
require('chromedriver')
const webdriver = require('selenium-webdriver')
exports.delay = function delay (time) {
return new Promise(resolve => setTimeout(resolve, time))
}
exports.buildWebDriver = function buildWebDriver (extPath) {
return new webdriver.Builder()
.withCapabilities({
chromeOptions: {
args: [`load-extension=${extPath}`],
},
})
.forBrowser('chrome')
.build()
}

@ -0,0 +1,230 @@
const path = require('path')
const fs = require('fs')
const pify = require('pify')
const mkdirp = require('mkdirp')
const rimraf = require('rimraf')
const webdriver = require('selenium-webdriver')
const endOfStream = require('end-of-stream')
const GIFEncoder = require('gifencoder')
const pngFileStream = require('png-file-stream')
const sizeOfPng = require('image-size/lib/types/png')
const By = webdriver.By
const { delay, buildWebDriver } = require('./func')
const localesIndex = require('../../app/_locales/index.json')
let driver
captureAllScreens().catch((err) => {
try {
console.error(err)
verboseReportOnFailure()
driver.quit()
} catch (err) {
console.error(err)
}
process.exit(1)
})
async function captureAllScreens() {
let screenshotCount = 0
// common names
let button
let tabs
let element
await cleanScreenShotDir()
// setup selenium and install extension
const extPath = path.resolve('dist/chrome')
driver = buildWebDriver(extPath)
await driver.get('chrome://extensions-frame')
const elems = await driver.findElements(By.css('.extension-list-item-wrapper'))
const extensionId = await elems[1].getAttribute('id')
await driver.get(`chrome-extension://${extensionId}/home.html`)
await delay(500)
tabs = await driver.getAllWindowHandles()
await driver.switchTo().window(tabs[0])
await delay(1000)
await setProviderType('localhost')
await delay(300)
// click try new ui
await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-center.flex-grow > p')).click()
await delay(300)
// close metamask homepage and extra home.html
tabs = await driver.getAllWindowHandles()
// metamask homepage is opened on prod, not dev
if (tabs.length > 2) {
await driver.switchTo().window(tabs[2])
driver.close()
}
await driver.switchTo().window(tabs[1])
driver.close()
await driver.switchTo().window(tabs[0])
await delay(300)
await captureLanguageScreenShots('welcome-new-ui')
// setup account
await delay(1000)
await driver.findElement(By.css('body')).click()
await delay(300)
await captureLanguageScreenShots('welcome')
await driver.findElement(By.css('button')).click()
await captureLanguageScreenShots('create password')
const passwordBox = await driver.findElement(By.css('input[type=password]:nth-of-type(1)'))
const passwordBoxConfirm = await driver.findElement(By.css('input[type=password]:nth-of-type(2)'))
passwordBox.sendKeys('123456789')
passwordBoxConfirm.sendKeys('123456789')
await delay(500)
await captureLanguageScreenShots('choose-password-filled')
await driver.findElement(By.css('button')).click()
await delay(500)
await captureLanguageScreenShots('unique account image')
await driver.findElement(By.css('button')).click()
await delay(500)
await captureLanguageScreenShots('privacy note')
await driver.findElement(By.css('button')).click()
await delay(300)
await captureLanguageScreenShots('terms')
await delay(300)
element = driver.findElement(By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', element)
await delay(300)
await captureLanguageScreenShots('terms-scrolled')
await driver.findElement(By.css('button')).click()
await delay(300)
await captureLanguageScreenShots('secret backup phrase')
await driver.findElement(By.css('button')).click()
await delay(300)
await captureLanguageScreenShots('secret backup phrase')
await driver.findElement(By.css('.backup-phrase__reveal-button')).click()
await delay(300)
await captureLanguageScreenShots('secret backup phrase - reveal')
await driver.findElement(By.css('button')).click()
await delay(300)
await captureLanguageScreenShots('confirm secret backup phrase')
// finish up
console.log('building gif...')
await generateGif()
await driver.quit()
return
//
// await button.click()
// await delay(700)
// this.seedPhase = await driver.findElement(By.css('.twelve-word-phrase')).getText()
// await captureScreenShot('seed phrase')
//
// const continueAfterSeedPhrase = await driver.findElement(By.css('button'))
// await continueAfterSeedPhrase.click()
// await delay(300)
// await captureScreenShot('main screen')
//
// await driver.findElement(By.css('.sandwich-expando')).click()
// await delay(500)
// await captureScreenShot('menu')
// await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)')).click()
// await captureScreenShot('main screen')
// it('should accept account password after lock', async () => {
// await delay(500)
// await driver.findElement(By.id('password-box')).sendKeys('123456789')
// await driver.findElement(By.css('button')).click()
// await delay(500)
// })
//
// it('should show QR code option', async () => {
// await delay(300)
// await driver.findElement(By.css('.fa-ellipsis-h')).click()
// await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div:nth-child(1) > flex-column > div.name-label > div > span > i > div > div > li:nth-child(3)')).click()
// await delay(300)
// })
//
// it('should show the account address', async () => {
// this.accountAddress = await driver.findElement(By.css('.ellip-address')).getText()
// await driver.findElement(By.css('.fa-arrow-left')).click()
// await delay(500)
// })
async function captureLanguageScreenShots(label) {
const nonEnglishLocales = localesIndex.filter(localeMeta => localeMeta.code !== 'en')
// take english shot
await captureScreenShot(`${label} (en)`)
for (let localeMeta of nonEnglishLocales) {
// set locale and take shot
await setLocale(localeMeta.code)
await delay(300)
await captureScreenShot(`${label} (${localeMeta.code})`)
}
// return locale to english
await setLocale('en')
await delay(300)
}
async function setLocale(code) {
await driver.executeScript('window.metamask.updateCurrentLocale(arguments[0])', code)
}
async function setProviderType(type) {
await driver.executeScript('window.metamask.setProviderType(arguments[0])', type)
}
// cleanup
await driver.quit()
async function cleanScreenShotDir() {
await pify(rimraf)(`./test-artifacts/screens/`)
}
async function captureScreenShot(label) {
const shotIndex = screenshotCount.toString().padStart(4, '0')
screenshotCount++
const artifactDir = `./test-artifacts/screens/`
await pify(mkdirp)(artifactDir)
// capture screenshot
const screenshot = await driver.takeScreenshot()
await pify(fs.writeFile)(`${artifactDir}/${shotIndex} - ${label}.png`, screenshot, { encoding: 'base64' })
}
async function generateGif(){
// calculate screenshot size
const screenshot = await driver.takeScreenshot()
const pngBuffer = Buffer.from(screenshot, 'base64')
const size = sizeOfPng.calculate(pngBuffer)
// read only the english pngs into gif
const encoder = new GIFEncoder(size.width, size.height)
const stream = pngFileStream('./test-artifacts/screens/* (en).png')
.pipe(encoder.createWriteStream({ repeat: 0, delay: 1000, quality: 10 }))
.pipe(fs.createWriteStream('./test-artifacts/screens/walkthrough (en).gif'))
// wait for end
await pify(endOfStream)(stream)
}
}
async function verboseReportOnFailure(test) {
const artifactDir = `./test-artifacts/${test.title}`
const filepathBase = `${artifactDir}/test-failure`
await pify(mkdirp)(artifactDir)
// capture screenshot
const screenshot = await driver.takeScreenshot()
await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
// capture dom source
const htmlSource = await driver.getPageSource()
await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource)
}

@ -1300,7 +1300,7 @@ function retryTransaction (txId) {
function setProviderType (type) {
return (dispatch) => {
log.debug(`background.setProviderType`)
log.debug(`background.setProviderType`, type)
background.setProviderType(type, (err, result) => {
if (err) {
log.error(err)
@ -1321,13 +1321,14 @@ function updateProviderType (type) {
}
function setRpcTarget (newRpc) {
log.debug(`background.setRpcTarget: ${newRpc}`)
return (dispatch) => {
log.debug(`background.setRpcTarget: ${newRpc}`)
background.setCustomRpc(newRpc, (err, result) => {
if (err) {
log.error(err)
return dispatch(self.displayWarning('Had a problem changing networks!'))
}
dispatch(actions.setSelectedToken())
})
}
}

@ -203,18 +203,18 @@ NetworkDropdown.prototype.render = function () {
{
key: 'default',
closeMenu: () => this.props.hideNetworkDropdown(),
onClick: () => props.setRpcTarget('http://localhost:8545'),
onClick: () => props.setProviderType('localhost'),
style: dropdownMenuItemStyle,
},
[
activeNetwork === 'http://localhost:8545' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
providerType === 'localhost' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
h(NetworkDropdownIcon, {
isSelected: activeNetwork === 'http://localhost:8545',
isSelected: providerType === 'localhost',
innerBorder: '1px solid #9b9b9b',
}),
h('span.network-name-item', {
style: {
color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b',
color: providerType === 'localhost' ? '#ffffff' : '#9b9b9b',
},
}, this.context.t('localhost')),
]

@ -69,6 +69,16 @@ async function startApp (metamaskState, accountManager, opts) {
store.dispatch(actions.updateMetamaskState(metamaskState))
})
// global metamask api - used by tooling
global.metamask = {
updateCurrentLocale: (code) => {
store.dispatch(actions.updateCurrentLocale(code))
},
setProviderType: (type) => {
store.dispatch(actions.setProviderType(type))
},
}
// start app
render(
h(Root, {

Loading…
Cancel
Save