server/desktop: remove download/install update option

This commit is contained in:
Chris Breiding
2017-06-22 09:21:13 -04:00
parent 61da10ac3f
commit 03476cda4f
25 changed files with 36 additions and 1160 deletions

View File

@@ -49,7 +49,6 @@ describe "App", ->
context "on:menu:clicked", ->
beforeEach ->
cy.stub(@ipc, "logOut")
cy.stub(@ipc, "windowOpen")
@start()
@@ -57,28 +56,6 @@ describe "App", ->
@ipc.onMenuClicked.yield(null, "log:out")
expect(@ipc.logOut).to.be.called
it "checks for updates", ->
@ipc.onMenuClicked.yield(null, "check:for:updates")
expect(@ipc.windowOpen).to.be.calledWithExactly({
position: "center",
width: 300,
height: 240,
toolbar: false,
title: "Updates",
type: "UPDATES",
})
context "on updates being applied", ->
beforeEach ->
@ipc.getOptions.resolves({"updating": true})
@start()
it "shows updates being applied view", ->
cy
.get("#login").contains("Applying updates")
.get("img").should("have.attr", "src", "img/cypress-inverse.png")
context "getting current user", ->
beforeEach ->
@getCurrentUser = @util.deferred()

View File

@@ -18,7 +18,7 @@ describe "Update Banner", ->
describe "general behavior", ->
beforeEach ->
cy.stub(@ipc, "getOptions").resolves({})
cy.stub(@ipc, "getOptions").resolves({version: "1.3.3"})
@start()
it "does not display update banner when no update available", ->
@@ -42,41 +42,26 @@ describe "Update Banner", ->
@updaterCheck.reject({name: "foo", message: "Something bad happened"})
cy.get(".footer").should("be.visible")
describe "in global mode", ->
beforeEach ->
cy.stub(@ipc, "getOptions").resolves({})
@start()
it "opens updates window on click of Update link", ->
@updaterCheck.resolve("1.3.4")
cy.contains("Update").click().then ->
expect(@ipc.windowOpen).to.be.calledWith({
position: "center"
width: 300
height: 240
toolbar: false
title: "Updates"
type: "UPDATES"
})
describe "in project mode", ->
beforeEach ->
cy.stub(@ipc, "getOptions").resolves({projectPath: "/foo/bar"})
@start()
@updaterCheck.resolve("1.3.4")
it "opens modal with info on click of Update link", ->
it "opens modal on click of Update link", ->
cy.contains("Update").click()
cy.get(".modal").should("be.visible")
describe "update modal", ->
beforeEach ->
cy.contains("Update").click()
it "closes modal when X is clicked", ->
cy.contains("Update").click()
cy.get(".close").click()
cy.get(".modal").should("not.be.visible")
it.only "opens changelog when Changelog is clicked", ->
cy.get(".modal").contains("Changelog").click().then =>
expect(@ipc.externalOpen).to.be.calledWith("https://on.cypress.io/changelog")
describe "in project mode", ->
beforeEach ->
cy.stub(@ipc, "getOptions").resolves({version: "1.3.3", projectPath: "/foo/bar"})
@start()
@updaterCheck.resolve("1.3.4")
cy.contains("Update").click()
it "closes modal when X is clicked", ->
cy.get(".close").click()
cy.get(".modal").should("not.be.visible")
it.only "modal has info about updating package.json", ->
cy.get(".modal").contains("Quit this app")
cy.get(".modal").contains("npm install -D cypress@1.3.4")
it "opens changelog when Changelog is clicked", ->
cy.get(".modal").contains("Changelog").click().then =>
expect(@ipc.externalOpen).to.be.calledWith("https://on.cypress.io/changelog")

View File

@@ -1,80 +0,0 @@
describe "Updates", ->
beforeEach ->
cy.viewport(300, 240)
cy.visit("/updates.html").then (win) =>
{ startUpdateApp, @ipc } = win.App
@version = "1.78"
cy.stub(@ipc, "getOptions").resolves({version: @version})
cy.stub(@ipc, "updaterRun")
cy.stub(@ipc, "externalOpen")
cy.stub(@ipc, "windowClose")
startUpdateApp()
it "updates title", ->
cy.title().should("include", "Updates")
it "displays loading spinner before updater:run is called", ->
cy.get(".loader").should("exist")
it "triggers updater:run", ->
expect(@ipc.updaterRun).to.be.called
it "links to Changelog", ->
@ipc.updaterRun.yield(null, {event: "none"})
cy.contains("a", "View Changelog").click().then ->
expect(@ipc.externalOpen).to.be.calledWith("https://on.cypress.io/changelog")
it "displays current version", ->
@ipc.updaterRun.yield(null, {event: "none"})
cy.get(".version").contains(@version)
describe "updater:run start", ->
it "displays check for updates msg", ->
@ipc.updaterRun.yield(null, {event: "start"})
cy.contains("Checking for updates...")
describe "updater:run apply", ->
it "displays applying updates msg", ->
@ipc.updaterRun.yield(null, {event: "apply"})
cy.contains("Applying updates...")
describe "updater:run error", ->
it "displays error msg", ->
@ipc.updaterRun.yield(null, {event: "error"})
cy.contains("An error occurred updating")
cy.contains("You can manually update Cypress by running 'cypress install' from your terminal or by downloading it again.")
it "triggers window:close on click of close btn", ->
@ipc.updaterRun.yield(null, {event: "error"})
cy.contains(".btn", "Done").click().then ->
expect(@ipc.windowClose).to.be.called
describe "updater:run none", ->
it "displays none msg", ->
@ipc.updaterRun.yield(null, {event: "none"})
cy.contains("No updates available")
it "triggers window:close on click of close btn", ->
@ipc.updaterRun.yield(null, {event: "none"})
cy.contains(".btn", "Done").click().then ->
expect(@ipc.windowClose).to.be.called
describe "updater:run download", ->
it "displays download msg", ->
@ipc.updaterRun.yield(null, {event: "download"})
cy.contains("Downloading updates...")
describe "updater:run done", ->
it "displays done msg", ->
@ipc.updaterRun.yield(null, {event: "done"})
cy.contains("Updates ready")
it "triggers window:close on click of restart btn", ->
@ipc.updaterRun.yield(null, {event: "done"})
cy.contains(".btn", "Restart").click().then ->
expect(@ipc.windowClose).to.be.called

View File

@@ -8,8 +8,4 @@ module.exports = {
getPathToIndex () {
return file('index.html')
},
getPathToUpdates () {
return file('updates.html')
},
}

View File

@@ -9,7 +9,6 @@ import appStore from '../lib/app-store'
import authStore from '../lib/auth-store'
import viewStore from '../lib/view-store'
import ApplyingUpdates from './applying-updates'
import Intro from './intro'
import Layout from './layout'
import Login from '../login/login'
@@ -27,21 +26,15 @@ class App extends Component {
projectPath: options.projectPath,
})
if (options.updating) {
viewStore.showApplyingUpdates()
// mobx can trigger a synchronous re-render, which executes
// componentDidMount, etc in other components, making bluebird
// think another promise was created but not returned
// return null to prevent bluebird warning about it
// same goes for other `return null`s below
return null
} else {
return ipc.getCurrentUser()
}
return ipc.getCurrentUser()
})
.then((user) => {
if (viewStore.isApplyingUpdates()) return
authStore.setUser(user)
// mobx can trigger a synchronous re-render, which executes
// componentDidMount, etc in other components, making bluebird
// think another promise was created but not returned
// return null to prevent bluebird warning about it
// same goes for other `return null`s below
return null
})
.catch(ipc.isUnauthed, () => {
@@ -56,8 +49,6 @@ class App extends Component {
return <Loader color='#888' scale={0.5} />
case C.LOGIN:
return <Login />
case C.APPLYING_UPDATES:
return <ApplyingUpdates />
case C.INTRO:
return (
<Layout>

View File

@@ -1,20 +0,0 @@
import React, { Component } from 'react'
export default class ApplyingUpdates extends Component {
render () {
return (
<div id='login'>
<div className='login-img-wrapper'>
<img src='img/cypress-inverse.png' />
</div>
<div className='login-content'>
<div className='login-spinner'>
<i className='fa fa-spinner fa-spin'></i>{' '}
Applying updates and restarting...
</div>
</div>
</div>
)
}
}

View File

@@ -1,6 +1,5 @@
import ipc from './ipc'
import authStore from './auth-store'
import updater from '../update/update-model'
const appApi = {
logOut () {
@@ -16,9 +15,6 @@ const appApi = {
case 'log:out':
this.logOut()
break
case 'check:for:updates':
updater.openUpdateWindow()
break
default:
return
}

View File

@@ -1,7 +1,6 @@
export default {
LOADING: 'LOADING',
LOGIN: 'LOGIN',
APPLYING_UPDATES: 'APPLYING_UPDATES',
INTRO: 'INTRO',
PROJECT_SPECS: 'PROJECT_SPECS',
PROJECT_RUNS: 'PROJECT_RUNS',

View File

@@ -9,16 +9,6 @@ class ViewStore {
name: C.LOADING,
}
@action showApplyingUpdates () {
this.currentView = {
name: C.APPLYING_UPDATES,
}
}
isApplyingUpdates () {
return this._isView(C.APPLYING_UPDATES)
}
@action showLoading () {
this.currentView = {
name: C.LOADING,

View File

@@ -7,7 +7,6 @@ import handleGlobalErrors from './lib/handle-global-errors'
import momentOverrides from './lib/configure-moment'
import App from './app/app'
import Updates from './update/updates'
useStrict(true)
@@ -24,8 +23,4 @@ window.App = {
start () {
render(<App />, document.getElementById('app'))
},
startUpdateApp () {
render(<Updates />, document.getElementById('updates'))
},
}

View File

@@ -4,7 +4,6 @@ import { observer } from 'mobx-react'
import BootstrapModal from 'react-bootstrap-modal'
import appStore from '../lib/app-store'
import updater from './update-model'
import ipc from '../lib/ipc'
@observer
@@ -31,7 +30,7 @@ class UpdateBanner extends Component {
return (
<div id='updates-available'>
New updates are available
<strong onClick={this._downloadUpdate}>
<strong onClick={() => this._toggleModal(true)}>
<i className='fa fa-download'></i>{' '}
Update
</strong>
@@ -50,21 +49,15 @@ class UpdateBanner extends Component {
<div className='update-modal modal-body os-dialog'>
<BootstrapModal.Dismiss className='btn btn-link close'>x</BootstrapModal.Dismiss>
<h4>Update to the latest version</h4>
<p>Version <strong>{appStore.newVersion}</strong> is now available (<a href='#' onClick={this._openChangelog}>Changelog</a>)</p>
<p>
Version <strong>{appStore.newVersion}</strong> is now available (currently running <strong>{appStore.version}</strong>).{' '}
<a href='#' onClick={this._openChangelog}>Changelog</a>
</p>
<ol>
<li>
Set <code>"cypress"</code> in your app's <code>package.json</code> to <code>"{appStore.newVersion}"</code>
<pre>
{'{\n'}
{' "devDependencies": {\n'}
{` "cypress": "${appStore.newVersion}"\n`}
{' }\n'}
{'}'}
</pre>
<li>Quit this app</li>
<li> Run <code>npm install -D cypress@{appStore.newVersion}</code>
</li>
<li>Quit this app and run <code>npm install</code></li>
<li>Run <code>cypress open</code> to re-open the new version of the app</li>
<li>Run <code>cypress open</code> to open the new version of the app</li>
</ol>
</div>
</BootstrapModal>
@@ -82,14 +75,6 @@ class UpdateBanner extends Component {
})
}
_downloadUpdate = () => {
if (appStore.isGlobalMode) {
updater.openUpdateWindow()
} else {
this._toggleModal(true)
}
}
@action _toggleModal = (showModal) => {
this.showingModal = showModal
}

View File

@@ -1,83 +0,0 @@
import { computed, observable, action } from 'mobx'
import ipc from '../lib/ipc'
class Updater {
@observable version
@observable state
@observable finished = false
@action setVersion (version) {
this.version = version
}
@action setNewVersion (newVersion) {
this.newVersion = newVersion
}
@action setState (state) {
switch (state) {
case 'error':
case 'done':
case 'none':
this.finished = true
this.state = state
break
default:
this.state = state
}
return this.state
}
@computed get stateFormatted () {
let result
switch (this.state) {
case 'checking':
result = 'Checking for updates...'
break
case 'downloading':
result = 'Downloading updates...'
break
case 'applying':
result = 'Applying updates...'
break
case 'done':
result = 'Updates ready'
break
case 'none':
result = 'No updates available'
break
case 'error':
result = 'An error occurred updating: \nYou can manually update Cypress by running \'cypress install\' from your terminal or by downloading it again.'
break
default:
result = ''
}
return result
}
@computed get buttonFormatted () {
if (this.state === 'done') {
return 'Restart'
} else {
return 'Done'
}
}
openUpdateWindow () {
return ipc.windowOpen({
position: 'center',
width: 300,
height: 240,
toolbar: false,
title: 'Updates',
type: 'UPDATES',
})
}
}
export default new Updater()

View File

@@ -1,129 +0,0 @@
import React, { Component } from 'react'
import { observer } from 'mobx-react'
import Loader from 'react-loader'
import ipc from '../lib/ipc'
import updater from './update-model'
const openChangelog = (e) => {
e.preventDefault()
return ipc.externalOpen('https://on.cypress.io/changelog')
}
@observer
class Updates extends Component {
componentDidMount () {
ipc.getOptions().then((options = {}) => {
updater.setVersion(options.version)
})
ipc.updaterRun((err, data = {}) => {
switch (data.event) {
case 'start':
return updater.setState('checking')
case 'apply':
return updater.setState('applying')
case 'error':
return updater.setState('error')
case 'done':
return updater.setState('done')
case 'none':
return updater.setState('none')
case 'download':
updater.setNewVersion(data.version)
return updater.setState('downloading')
default:
return
}
})
}
render () {
if (!updater.state) return <Loader color='#888' scale={0.5}/>
return (
<div>
<p>
<a onClick={openChangelog} href='#'>
View Changelog
</a>
</p>
{ this._currentVersion() }
{ this._newVersion() }
{ this._state() }
</div>
)
}
_currentVersion = () => {
if (updater.version) {
return (
<p className='version'>
<b>Current Version:</b>{' '}
<span>{ updater.version }</span>
</p>
)
}
}
_newVersion = () => {
if (updater.newVersion) {
return (
<p className='new-version'>
<b>New Version:</b>{' '}
<span>{ updater.newVersion }</span>
</p>
)
}
}
_state = () => {
let errClass
if (updater.state === 'error') {
errClass = 'text-danger'
}
if (updater.state) {
return (
<div>
<p className={`state ${errClass}`}>
{ this._notFinished() }{' '}
<span dangerouslySetInnerHTML={this._getHtmlState()} />
</p>
{ this._finished() }
</div>
)
}
}
_getHtmlState = () => {
return { __html: updater.stateFormatted.split('\n').join('<br />') }
}
_notFinished = () => {
if (!updater.finished) {
return (
<i className='fa fa-spinner fa-spin'></i>
)
}
}
_finished = () => {
if (updater.finished) {
return (
<div>
<button onClick={this._closeWindow} className='btn btn-default'>
{ updater.buttonFormatted }
</button>
</div>
)
}
}
_closeWindow (e) {
e.preventDefault()
ipc.windowClose()
}
}
export default Updates

View File

@@ -18,30 +18,12 @@
}
}
#updates {
text-align: center;
padding: 40px 0;
}
html.updates {
background-color: #ECECEC;
}
html.has-updates {
.helper-line {
bottom: 90px;
}
}
#updates-applied {
padding-top: 175px;
text-align: center;
h3 {
margin-bottom: 20px;
}
}
.update-modal {
ol {
padding-left: 20px;

View File

@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="updates">
<head>
<meta charset="utf-8" />
<title>Updates</title>
{{#each stylesheets}}
<link rel="stylesheet" href="{{this}}" />
{{/each}}
</head>
<body>
<div id="updates"></div>
<script src="vendor.js"></script>
{{#each scripts}}
<script src="{{this}}"></script>
{{/each}}
<script>
window.env = '{{env.NODE_ENV}}'
if (!window.Cypress) {
App.startUpdateApp()
}
</script>
</body>
</html>

View File

@@ -117,26 +117,6 @@ handleEvent = (options, bus, event, id, type, arg) ->
onNoNewVersion: -> send(false)
})
when "updater:run"
echo = (event, version) ->
send({event: event, version: version})
upd = Updater.run({
onStart: -> echo("start")
onApply: -> echo("apply")
onError: -> echo("error")
onDone: -> echo("done")
onNone: -> echo("none")
onDownload: (version) ->
echo("download", version)
})
## TODO: there is no note here, what if the window
## is closed once the updater finishes?
win = Windows.getByWebContents(event.sender)
win.once "closed", ->
upd.cancel()
when "get:logs"
logs.get()
.then(send)

View File

@@ -6,7 +6,6 @@ shell = require("electron").shell
appData = require("../util/app_data")
open = require("../util/open")
onUpdatesClicked = ->
onLogOutClicked = ->
module.exports = {
@@ -15,11 +14,8 @@ module.exports = {
withDevTools: false
})
## these are set by modes/headed.coffee and need to be preserved
## if the menu is set again by launcher.coffee when the Electron
## browser is run
if options.onUpdatesClicked
onUpdatesClicked = options.onUpdatesClicked
## this set by modes/headed.coffee and needs to be preserved if the menu
## is set again by launcher.coffee when the Electron browser is run
if options.onLogOutClicked
onLogOutClicked = options.onLogOutClicked
@@ -32,10 +28,6 @@ module.exports = {
click: ->
shell.openExternal("https://on.cypress.io/changelog")
}
{
label: "Check for Updates"
click: onUpdatesClicked
}
{
type: "separator"
}

View File

@@ -20,8 +20,6 @@ getUrl = (type) ->
cyDesktop.getPathToAbout()
when "DEBUG"
cyDesktop.getPathToDebug()
when "UPDATES"
cyDesktop.getPathToUpdates()
when "INDEX"
cyDesktop.getPathToIndex()
else

View File

@@ -9,7 +9,6 @@ cyIcons = require("@cypress/icons")
user = require("../user")
errors = require("../errors")
savedState = require("../saved_state")
Updater = require("../updater")
logs = require("../gui/logs")
menu = require("../gui/menu")
Events = require("../gui/events")
@@ -78,9 +77,6 @@ module.exports = {
## instance here instead of callback functions
menu.set({
withDevTools: isDev()
onUpdatesClicked: ->
bus.emit("menu:item:clicked", "check:for:updates")
onLogOutClicked: ->
bus.emit("menu:item:clicked", "log:out")
})
@@ -97,9 +93,6 @@ module.exports = {
Events.start(options, bus)
if options.updating
Updater.install(options)
return win
run: (options) ->

View File

@@ -1,23 +1,11 @@
_ = require("lodash")
os = require("os")
fs = require("fs-extra")
tar = require("tar-fs")
nmi = require("node-machine-id")
path = require("path")
glob = require("glob")
home = require("home-or-tmp")
trash = require("trash")
chmodr = require("chmodr")
semver = require("semver")
request = require("request")
Promise = require("bluebird")
NwUpdater = require("node-webkit-updater")
cwd = require("./cwd")
konfig = require("./konfig")
logger = require("./logger")
argsUtil = require("./util/args")
chmodr = Promise.promisify(chmodr)
## backup the original cwd
localCwd = cwd()
@@ -68,29 +56,11 @@ class Updater
@client = new NwUpdater @getPackage()
@request = null
@cancelled = false
@callbacks = callbacks
if process.env["CYPRESS_ENV"] isnt "production"
@patchAppPath()
getArgs: ->
c = @getClient()
## get a reference to current cwd
currentCwd = process.cwd()
## change to the original local cwd
## in case we have a project open
process.chdir(localCwd)
args = _.compact ["--app-path=" + c.getAppPath(), "--exec-path=" + c.getAppExec(), "--updating"]
## now restore back to what it was
process.chdir(currentCwd)
return args
patchAppPath: ->
@getClient().getAppPath = -> cwd()
@@ -103,152 +73,6 @@ class Updater
## requiring inline due to easier testability
@client ? throw new Error("missing Updater#client")
trash: (appPath) ->
## moves the current appPath to the trash
## this is the path to the existing app
logger.info "trashing current app", appPath: appPath
Promise.resolve(trash([appPath]))
install: (argsObj = {}) ->
c = @getClient()
argsObj = @normalizeArgs(argsObj)
{appPath, execPath} = argsObj
## slice out updating, execPath, and appPath args
argsObj = _.omit(argsObj, "updating", "execPath", "appPath")
args = argsUtil.toArray(argsObj)
## trash the 'old' app currently installed at the default
## installation: /Applications/Cypress.app
@trash(appPath).then =>
logger.info "installing updated app", appPath: appPath, execPath: execPath
@copyTmpToAppPath(c.getAppPath(), appPath).then ->
c.run(execPath, args)
## and now quit this process
process.exit(0)
normalizeArgs: (args = {}) ->
## argsObj should look like this
##
## OSX:
## {
## updating: true
## appPath: "/Applications/Cypress.app"
## execPath: "/Applications/Cypress.app"
## }
##
## Linux:
## {
## updating: true
## appPath: "/home/vagrant/.cypress/Cypress"
## execPath: "/home/vagrant/.cypress/Cypress/Cypress"
## }
##
## there was a bug in Cypress in the 0.17.x range
## which due to changing process.cwd() we messed
## up the app-path + exec-path and were accidentally
## trying to trash 3 levels above the users project!
## so let's detect that and fix it right here with
## the defaults.
##
{appPath, execPath} = args
switch os.platform()
when "darwin"
if not osxAppRe.test(appPath)
args.appPath = args.execPath = "/Applications/Cypress.app"
when "linux"
if not linuxAppRe.test(appPath)
p = path.join(@getHome(), ".cypress", "Cypress")
args.appPath = p
args.execPath = path.join(p, "Cypress")
return args
getHome: -> home
copyTmpToAppPath: (tmp, appPath) ->
new Promise (resolve, reject) ->
obj = {
fs: require("original-fs")
}
## now move the /tmp application over
## to the 'existing / old' app path.
## meaning from from /tmp/Cypress.app to /Applications/Cypress.app
## https://github.com/atom/electron/pull/3641
## tar-fs
tar
.pack(tmp, obj)
.pipe(tar.extract(appPath, obj))
.on "error", reject
.on "finish", resolve
runInstaller: (newAppPath) ->
## get the --updating args
args = @getArgs()
logger.info "running installer from tmp", destination: newAppPath, args: args
## runs the 'new' app in the /tmp directory with
## appPath + execPath to the 'existing / old' app
## (which is where its normally installed in /Applications)
## it additionally passes the --updating flag
@getClient().runInstaller(newAppPath, args, {})
process.exit()
unpack: (destinationPath, manifest) ->
logger.info "unpacking new version", destination: destinationPath
@trigger("apply")
fn = (err, newAppPath) =>
return @trigger("error", err) if err
return if @cancelled
@runInstaller(newAppPath)
@getClient().unpack(destinationPath, fn, manifest)
download: (manifest) ->
logger.info "downloading new version", version: manifest.version
@trigger("download", manifest.version)
fn = (err, destinationPath) =>
return @trigger("error", err) if err
## fixes issue with updater failing during unpack
setTimeout =>
return if @cancelled
@unpack(destinationPath, manifest)
, 1000
@request = @getClient().download(fn, manifest)
cancel: ->
@cancelled = true
try
## attempt to abort the request
## and slurp up any errors
@request.abort()
trigger: (event, args...) ->
## normalize event name
event = "on" + event[0].toUpperCase() + event.slice(1)
@@ -268,25 +92,7 @@ class Updater
logger.info "new version does not exist"
options.onNoNewVersion?()
run: ->
@trigger("start")
@check
onNewVersion: (manifest) =>
@download(manifest)
onNoNewVersion: =>
@trigger("none")
return @
@install = (options) ->
Updater().install(options)
@check = (options = {}) ->
Updater().check(options)
@run = (callbacks = {}) ->
Updater(callbacks).run()
module.exports = Updater

View File

@@ -85,7 +85,6 @@
"bytes": "^2.4.0",
"chai": "^1.9.2",
"chalk": "1.1.1",
"chmodr": "^0.1.0",
"chokidar": "1.6.0",
"cjsxify": "^0.3.0",
"compression": "1.1.0",
@@ -108,7 +107,6 @@
"graceful-fs": "^4.1.11",
"gulp-util": "^3.0.6",
"hbs": "4.0.0",
"home-or-tmp": "^2.0.0",
"http-accept": "^0.1.6",
"http-proxy": "^1.13.2",
"http-status-codes": "^1.0.6",

View File

@@ -193,35 +193,6 @@ describe "lib/gui/events", ->
@handleEvent("updater:check")
@expectSendCalledWith(false)
describe "updater:run", ->
beforeEach ->
@once = @sandbox.stub()
@sandbox.stub(Windows, "getByWebContents").withArgs(@event.sender).returns({once: @once})
it "calls cancel when win is closed", ->
cancel = @sandbox.spy()
run = @sandbox.stub(Updater, "run").returns({cancel: cancel})
@once.withArgs("closed").yields()
@handleEvent("updater:run")
expect(cancel).to.be.calledOnce
_.each {
onStart: "start"
onApply: "apply"
onError: "error"
onDone: "done"
onNone: "none"
}, (val, key) ->
it "returns #{val} on #{key}", ->
@sandbox.stub(Updater, "run").yieldsTo(key)
@handleEvent("updater:run")
@expectSendCalledWith({event: val, version: undefined})
it "returns down + version on onDownload", ->
@sandbox.stub(Updater, "run").yieldsTo("onDownload", "0.14.0")
@handleEvent("updater:run")
@expectSendCalledWith({event: "download", version: "0.14.0"})
context "log events", ->
describe "get:logs", ->
it "returns array of logs", ->

View File

@@ -71,13 +71,12 @@ describe "gui/menu", ->
expect(getMenuItem("Cypress")).to.be.undefined
context "File", ->
it "contains changelog, check for updates, logout, close window", ->
it "contains changelog, logout, close window", ->
menu.set()
labels = getLabels(getMenuItem("File").submenu)
expect(labels).to.eql([
"Changelog"
"Check for Updates"
"Manage Account"
"Log Out"
"View App Data"
@@ -89,23 +88,6 @@ describe "gui/menu", ->
getSubMenuItem(getMenuItem("File"), "Changelog").click()
expect(electron.shell.openExternal).to.be.calledWith("https://on.cypress.io/changelog")
it "calls updates callback when Check for Updates is clicked", ->
onUpdatesClicked = @sandbox.stub()
menu.set({onUpdatesClicked})
getSubMenuItem(getMenuItem("File"), "Check for Updates").click()
expect(onUpdatesClicked).to.be.called
it "calls original updates callback when menu is reset without new callback", ->
onUpdatesClicked = @sandbox.stub()
menu.set({onUpdatesClicked})
menu.set()
getSubMenuItem(getMenuItem("File"), "Check for Updates").click()
expect(onUpdatesClicked).to.be.called
it "is noop when Check for Updates is clicked with no callback", ->
menu.set()
expect(-> getSubMenuItem(getMenuItem("File"), "Check for Updates").click()).not.to.throw()
it "opens dashboard when Manage Account is clicked", ->
menu.set()
getSubMenuItem(getMenuItem("File"), "Manage Account").click()

View File

@@ -92,7 +92,6 @@ describe "gui/headed", ->
@sandbox.stub(menu, "set")
@sandbox.stub(Events, "start")
@sandbox.stub(Updater, "install")
@sandbox.stub(Windows, "open").resolves(@win)
@sandbox.stub(Windows, "trackState")
@@ -105,14 +104,6 @@ describe "gui/headed", ->
headed.ready(opts).then ->
expect(Events.start).to.be.calledWith(opts)
it "calls Updater.install if options.updating", ->
headed.ready({updating: true}).then ->
expect(Updater.install).to.be.calledOnce
it "does not call Updater.install", ->
headed.ready({}).then ->
expect(Updater.install).not.to.be.called
it "calls menu.set", ->
headed.ready({}).then ->
expect(menu.set).to.be.calledOnce

View File

@@ -2,14 +2,10 @@ require("../spec_helper")
delete global.fs
os = require("os")
tar = require("tar-fs")
nmi = require("node-machine-id")
cwd = require("#{root}lib/cwd")
home = require("home-or-tmp")
request = require("request")
Updater = require("#{root}lib/updater")
Fixtures = require("#{root}/test/support/helpers/fixtures")
describe "lib/updater", ->
context "interface", ->
@@ -41,104 +37,6 @@ describe "lib/updater", ->
client2 = u.getClient()
expect(client).to.eq(client2)
context "#normalizeArgs", ->
beforeEach ->
@env = process.env.CYPRESS_ENV
process.env.CYPRESS_ENV = "production"
@updater = Updater({})
@updater.getClient()
afterEach ->
process.env.CYPRESS_ENV = @env
it "resets appPath + execPath on OSX", ->
@sandbox.stub(os, "platform").returns("darwin")
expect(@updater.normalizeArgs({
appPath: "/Users/bmann/Dev"
execPath: "/Users/bmann/Dev"
})).to.deep.eq({
appPath: "/Applications/Cypress.app"
execPath: "/Applications/Cypress.app"
})
it "does not reset appPath + execPath on OSX", ->
@sandbox.stub(os, "platform").returns("darwin")
expect(@updater.normalizeArgs({
appPath: "/foo/Cypress.app"
execPath: "/foo/Cypress.app"
})).to.deep.eq({
appPath: "/foo/Cypress.app"
execPath: "/foo/Cypress.app"
})
it "resets appPath + execPath on linux", ->
@sandbox.stub(os, "platform").returns("linux")
@sandbox.stub(@updater, "getHome").returns("/home/vagrant")
expect(@updater.normalizeArgs({
appPath: "/Users/bmann/Dev"
execPath: "/Users/bmann/Dev"
})).to.deep.eq({
appPath: "/home/vagrant/.cypress/Cypress"
execPath: "/home/vagrant/.cypress/Cypress/Cypress"
})
it "does not reset appPath + execPath on linux", ->
@sandbox.stub(os, "platform").returns("linux")
expect(@updater.normalizeArgs({
appPath: "/foo/Cypress"
execPath: "/foo/Cypress/Cypress"
})).to.deep.eq({
appPath: "/foo/Cypress"
execPath: "/foo/Cypress/Cypress"
})
context "#getHome", ->
beforeEach ->
@updater = Updater({})
it "returns home-or-tmp", ->
expect(@updater.getHome()).to.eq(home)
context "#getArgs", ->
beforeEach ->
@updater = Updater({})
@updater.getClient()
@sandbox.stub(@updater.client, "getAppExec").returns("bar")
it "compacts null values", ->
@sandbox.stub(@updater.client, "getAppPath").returns("foo")
expect(@updater.getArgs()).to.deep.eq ["--app-path=foo", "--exec-path=bar", "--updating"]
## TODO: this is failing in local docker but lib/updater
## is being removed in 0.20.0, so it doesn't really matter
it.skip "changes cwd to be the original + then restores", ->
tmp = os.tmpDir()
## manually change process.cwd
process.chdir(tmp)
localCwd = cwd()
fn = ->
process.cwd() + "/baz"
@sandbox.stub(@updater.client, "getAppPath", fn)
args = @updater.getArgs()
expect(args).to.deep.eq(["--app-path=#{localCwd + "/baz"}", "--exec-path=bar", "--updating"])
expect(args[0]).not.to.include(tmp)
expect(process.cwd()).to.include(tmp)
expect(process.cwd()).not.to.include(localCwd)
context "#checkNewVersion", ->
beforeEach ->
@get = @sandbox.spy(request, "get")
@@ -169,299 +67,6 @@ describe "lib/updater", ->
done()
context ".run", ->
beforeEach ->
@updater = Updater({})
@updater.getClient()
@checkNewVersion = @sandbox.stub(@updater.client, "checkNewVersion")
@sandbox.stub(process, "exit")
it "invokes onRun", ->
spy = @sandbox.spy()
Updater.run({onStart: spy})
expect(spy).to.be.called
describe "client#checkNewVersion", ->
beforeEach ->
@download = @sandbox.stub(Updater.prototype, "download")
it "is called once", ->
@updater.run()
expect(@checkNewVersion).to.be.calledOnce
it "calls #download if new version exists", ->
@checkNewVersion.yields(null, true, {})
@updater.run()
expect(@download).to.be.calledOnce
it "passes manifest to #download when new version exists", ->
@checkNewVersion.yields(null, true, {foo: "bar"})
@updater.run()
expect(@download).to.be.calledWith({foo: "bar"})
it "does not call #download if there isnt a new version", ->
@checkNewVersion.yields(null, false, {foo: "bar"})
@updater.run()
expect(@download).not.to.be.called
it "invokes onNone when there isnt a new version", ->
spy = @sandbox.spy()
@checkNewVersion.yields(null, false)
@updater.callbacks.onNone = spy
@updater.run()
expect(spy).to.be.called
it "does not call #download if there is an error", ->
@checkNewVersion.yields((new Error()), true, {foo: "bar"})
@updater.run()
expect(@download).not.to.be.called
it "invokes onError callbacks", ->
err = new Error("checking onError")
spy = @sandbox.spy()
@checkNewVersion.yields(err)
@updater.callbacks.onError = spy
@updater.run()
expect(spy).to.be.calledWith(err)
describe "#download", ->
beforeEach ->
@sandbox.stub(@updater.client, "download")
@sandbox.stub(@updater, "unpack")
@clock = @sandbox.useFakeTimers()
it "invokes onDownload", ->
spy = @sandbox.spy()
@updater.callbacks.onDownload = spy
@updater.download({})
@clock.tick(1000)
expect(spy).to.be.called
it "calls unpack with destinationPath and manifest", ->
@updater.client.download.yields(null, "/Users/bmann/app")
@updater.download({})
@clock.tick(1000)
expect(@updater.unpack).to.be.calledOnce.and.to.be.calledWith("/Users/bmann/app", {})
it "does not call unpack on error", ->
@updater.client.download.yields((new Error()), "/Users/bmann/app")
@updater.download({})
@clock.tick(1000)
expect(@updater.unpack).not.to.be.called
it "invokes onError callbacks", ->
err = new Error("checking onError")
spy = @sandbox.spy()
@updater.callbacks.onError = spy
@updater.client.download.yields(err)
@updater.download({})
@clock.tick(1000)
expect(spy).to.be.calledWith(err)
describe "#unpack", ->
beforeEach ->
@sandbox.stub(@updater.client, "unpack")
@sandbox.stub(@updater, "runInstaller")
it "invokes onApply", ->
spy = @sandbox.spy()
@updater.callbacks.onApply = spy
@updater.unpack("/some/path", {})
expect(spy).to.be.called
it "calls runInstaller with newAppPath", ->
@updater.client.unpack.yields(null, "/Users/bmann/app")
@updater.unpack("/some/path", {})
expect(@updater.runInstaller).to.be.calledOnce.and.to.be.calledWith("/Users/bmann/app")
it "does not call runInstaller on error", ->
@updater.client.unpack.yields((new Error()), "/Users/bmann/app")
@updater.unpack("/some/path", {})
expect(@updater.runInstaller).not.to.be.called
it "invokes onError callbacks", ->
err = new Error("checking onError")
spy = @sandbox.spy()
@updater.callbacks.onError = spy
@updater.client.unpack.yields(err)
@updater.unpack("/some/path", {})
expect(spy).to.be.calledWith(err)
describe "#runInstaller", ->
beforeEach ->
@sandbox.stub(@updater.client, "runInstaller")
# @sandbox.stub(@updater, "copyCyDataTo").resolves()
it "calls process.exit", ->
@updater.runInstaller("/Users/bmann/newApp")
expect(process.exit).to.be.calledOnce
it "calls runInstaller on the client", ->
c = @updater.client
@updater.runInstaller("/Users/bmann/newApp")
expect(@updater.client.runInstaller).to.be.calledWith("/Users/bmann/newApp", ["--app-path=#{c.getAppPath()}", "--exec-path=#{c.getAppExec()}", "--updating"], {})
## we no longer pass up additional App argv
## other than from debug i'm not sure why we
## would have ever wanted to do this. in fact
## its caused a bug in parseArgs and its duplicated
## every existing argument
it "does not pass along additional App argv", ->
c = @updater.client
@updater.runInstaller("/Users/bmann/newApp")
expect(@updater.client.runInstaller).to.be.calledWith("/Users/bmann/newApp", ["--app-path=#{c.getAppPath()}", "--exec-path=#{c.getAppExec()}", "--updating"], {})
describe "#copyCyDataTo", ->
beforeEach ->
fs.outputJsonAsync(".cy/cache", {foo: "bar"}).then ->
fs.outputFileAsync(".cy/foo/bar.txt", "foo!").then ->
fs.outputJsonAsync("new/app/path/Contents/Resources/app/package.json", {})
afterEach ->
fs.removeAsync("new").then ->
fs.removeAsync(".cy/foo")
describe "#copyTmpToAppPath", ->
beforeEach ->
@onStub = @sandbox.stub()
@onStub.withArgs("error").returnsThis()
@onPipe = @sandbox.stub().returnsThis()
@sandbox.stub(tar, "pack").returns({
on: @onStub
pipe: @onPipe
})
@sandbox.stub(tar, "extract").returns("extractFoo")
it "calls tar.pack", ->
@onStub.withArgs("finish").yieldsAsync()
@updater.copyTmpToAppPath("a", "b").then ->
expect(tar.pack).to.be.calledWith("a", {
fs: require("original-fs")
})
it "calls tar.extract", ->
@onStub.withArgs("finish").yieldsAsync()
@updater.copyTmpToAppPath("a", "b").then ->
expect(tar.extract).to.be.calledWith("b", {
fs: require("original-fs")
})
it "pipes return value of tar.extract", ->
@onStub.withArgs("finish").yieldsAsync()
@updater.copyTmpToAppPath("a", "b").then =>
expect(@onPipe).to.be.calledWith("extractFoo")
it "throws on error", ->
err = new Error("foo")
@onStub.withArgs("error").yieldsAsync(err)
@updater.copyTmpToAppPath("a", "b")
.then ->
throw new Error("should have failed but did not")
.catch (e) ->
expect(e).to.eq(err)
describe "#trash", ->
beforeEach ->
fs.outputFileAsync("random/dirs/and/file.txt", "foobarbaz!")
it "moves directory to trash", (done) ->
@updater.trash("random").then ->
fs.statAsync("random")
.then -> done("random should not exist!")
.catch -> done()
describe "#install", ->
beforeEach ->
@argsObj = {
appPath: "/Users/bmann/app_path"
execPath: "/Users/bmann/app_exec_path"
}
@normalizedArgs = @updater.normalizeArgs(@argsObj)
@sandbox.stub(@updater.client, "getAppPath").returns("foo")
@sandbox.stub(@updater, "copyTmpToAppPath").resolves()
@run = @sandbox.stub(@updater.client, "run")
@trash = @sandbox.stub(@updater, "trash").resolves()
it "trashes current appPath", ->
@updater.install(@argsObj).then =>
expect(@trash).to.be.calledWith(@normalizedArgs.appPath)
it "calls #copyTmpToAppPath with tmp + appPath", ->
@updater.install(@argsObj).then =>
expect(@updater.copyTmpToAppPath).to.be.calledWith "foo", @normalizedArgs.appPath
it "calls process.exit", ->
@updater.install(@argsObj).then =>
expect(process.exit).to.be.calledOnce
it "calls client.run with execPath + args", ->
@updater.install(@argsObj).then =>
expect(@run).to.be.calledWith(@argsObj.execPath, [])
context "args", ->
beforeEach ->
@argsObj = {
updating: true
appPath: "app"
execPath: "exec"
"getKey": true
}
@normalizedArgs = @updater.normalizeArgs(@argsObj)
it "uses args object", ->
@updater.install(@argsObj).then =>
expect(@run).to.be.calledWith(@normalizedArgs.execPath, ["--getKey=true"])
context "integration", ->
before ->
## 10 min timeout
@timeout(10 * 60 * 1000)
## ensure we have the cypress.zip fixture
Fixtures.ensureNwZip()
beforeEach ->
## force a lower package.json version
@sandbox.stub(fs, "readJsonSync").returns({version: "0.0.1"})
## force a manifest.json response here to be a slightly higher version
nock("http://download.cypress.io")
.get("/desktop.json")
.reply 200, {
name: "cypress"
version: "0.0.2"
packages: {
mac: {
url: "http://cdn.cypress.io/desktop/0.0.2/cypress.zip"
}
win: {
url: "http://cdn.cypress.io/desktop/0.0.2/cypress.zip"
}
linux: {
url: "http://cdn.cypress.io/desktop/0.0.2/cypress.zip"
}
}
}
.get("/dist.cypress.io/0.0.2/cypress.zip")
.reply 200, ->
fs.createReadStream Fixtures.path("nw/cypress.zip")
# it "runs", ->
# @updater = Updater({quit: @sandbox.spy()})
# @updater.run()
context "#check", ->
beforeEach ->
@updater = Updater({quit: @sandbox.spy()})