mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-29 19:41:16 -05:00
feat: Display command log entry for file downloads (#14749)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { create } from '../../../src/cypress/downloads'
|
||||
|
||||
describe('src/cypress/downloads', () => {
|
||||
let log
|
||||
let snapshot
|
||||
let end
|
||||
let downloads
|
||||
let downloadItem = {
|
||||
id: '1',
|
||||
filePath: '/path/to/save/location.csv',
|
||||
url: 'http://localhost:1234/location.csv',
|
||||
mime: 'text/csv',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
end = cy.stub()
|
||||
snapshot = cy.stub().returns({ end })
|
||||
log = cy.stub().returns({ snapshot })
|
||||
|
||||
downloads = create({ log })
|
||||
})
|
||||
|
||||
context('#start', () => {
|
||||
it('creates snapshot for download', () => {
|
||||
downloads.start(downloadItem)
|
||||
expect(log).to.be.calledWithMatch({
|
||||
message: downloadItem.filePath,
|
||||
name: 'download',
|
||||
type: 'parent',
|
||||
event: true,
|
||||
timeout: 0,
|
||||
})
|
||||
|
||||
expect(snapshot).to.be.called
|
||||
})
|
||||
|
||||
it('consoleProps include download url, save path, and mime type', () => {
|
||||
downloads.start(downloadItem)
|
||||
const consoleProps = log.lastCall.args[0].consoleProps()
|
||||
|
||||
expect(consoleProps).to.eql({
|
||||
'Download URL': downloadItem.url,
|
||||
'Saved To': downloadItem.filePath,
|
||||
'Mime Type': downloadItem.mime,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#end', () => {
|
||||
it('ends snapshot if matching log exists', () => {
|
||||
downloads.start(downloadItem)
|
||||
downloads.end({ id: '1' })
|
||||
|
||||
expect(end).to.be.called
|
||||
})
|
||||
|
||||
it('is a noop if matching log does not exist', () => {
|
||||
downloads.end({ id: '1' })
|
||||
|
||||
expect(end).not.to.be.called
|
||||
// also just shouldn't error
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -24,6 +24,7 @@ const $LocalStorage = require('./cypress/local_storage')
|
||||
const $Mocha = require('./cypress/mocha')
|
||||
const $Mouse = require('./cy/mouse')
|
||||
const $Runner = require('./cypress/runner')
|
||||
const $Downloads = require('./cypress/downloads')
|
||||
const $Server = require('./cypress/server')
|
||||
const $Screenshot = require('./cypress/screenshot')
|
||||
const $SelectorPlayground = require('./cypress/selector_playground')
|
||||
@@ -54,6 +55,7 @@ class $Cypress {
|
||||
this.chai = null
|
||||
this.mocha = null
|
||||
this.runner = null
|
||||
this.downloads = null
|
||||
this.Commands = null
|
||||
this.$autIframe = null
|
||||
this.onSpecReady = null
|
||||
@@ -184,6 +186,7 @@ class $Cypress {
|
||||
this.log = $Log.create(this, this.cy, this.state, this.config)
|
||||
this.mocha = $Mocha.create(specWindow, this, this.config)
|
||||
this.runner = $Runner.create(specWindow, this.mocha, this, this.cy)
|
||||
this.downloads = $Downloads.create(this)
|
||||
|
||||
// wire up command create to cy
|
||||
this.Commands = $Commands.create(this, this.cy, this.state, this.config)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
export const create = (Cypress) => {
|
||||
const logs = {}
|
||||
|
||||
const start = (downloadItem) => {
|
||||
// store a reference to the download's log so we can retrieve it
|
||||
// and end the snapshot later when it's done
|
||||
const log = logs[downloadItem.id] = Cypress.log({
|
||||
message: downloadItem.filePath,
|
||||
name: 'download',
|
||||
type: 'parent',
|
||||
event: true,
|
||||
timeout: 0,
|
||||
consoleProps: () => {
|
||||
const consoleObj = {
|
||||
'Download URL': downloadItem.url,
|
||||
'Saved To': downloadItem.filePath,
|
||||
'Mime Type': downloadItem.mime,
|
||||
}
|
||||
|
||||
return consoleObj
|
||||
},
|
||||
})
|
||||
|
||||
return log.snapshot()
|
||||
}
|
||||
|
||||
const end = ({ id }) => {
|
||||
const log = logs[id]
|
||||
|
||||
if (log) {
|
||||
log.snapshot().end()
|
||||
|
||||
// don't need this anymore since the download has ended
|
||||
// and won't change anymore
|
||||
delete logs[id]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,25 @@ const connect = function (host, path, extraOpts) {
|
||||
})
|
||||
})
|
||||
|
||||
const listenToDownloads = once(() => {
|
||||
browser.downloads.onCreated.addListener((downloadItem) => {
|
||||
ws.emit('automation:push:request', 'create:download', {
|
||||
id: `${downloadItem.id}`,
|
||||
filePath: downloadItem.filename,
|
||||
mime: downloadItem.mime,
|
||||
url: downloadItem.url,
|
||||
})
|
||||
})
|
||||
|
||||
browser.downloads.onChanged.addListener((downloadDelta) => {
|
||||
if ((downloadDelta.state || {}).current !== 'complete') return
|
||||
|
||||
ws.emit('automation:push:request', 'complete:download', {
|
||||
id: `${downloadDelta.id}`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const fail = (id, err) => {
|
||||
return ws.emit('automation:response', id, {
|
||||
__error: err.message,
|
||||
@@ -74,6 +93,7 @@ const connect = function (host, path, extraOpts) {
|
||||
|
||||
ws.on('connect', () => {
|
||||
listenToCookieChanges()
|
||||
listenToDownloads()
|
||||
|
||||
return ws.emit('automation:client:connected')
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"permissions": [
|
||||
"cookies",
|
||||
"downloads",
|
||||
"tabs",
|
||||
"http://*/*",
|
||||
"https://*/*",
|
||||
|
||||
@@ -14,6 +14,14 @@ const browser = {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
downloads: {
|
||||
onCreated: {
|
||||
addListener () {},
|
||||
},
|
||||
onChanged: {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
windows: {
|
||||
getLastFocused () {},
|
||||
},
|
||||
@@ -101,14 +109,28 @@ describe('app/background', () => {
|
||||
this.httpSrv = http.createServer()
|
||||
this.server = socket.server(this.httpSrv, { path: '/__socket.io' })
|
||||
|
||||
return this.httpSrv.listen(PORT, done)
|
||||
this.onConnect = (callback) => {
|
||||
const client = background.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
client.on('connect', _.once(() => {
|
||||
callback(client)
|
||||
}))
|
||||
}
|
||||
|
||||
this.stubEmit = (callback) => {
|
||||
this.onConnect((client) => {
|
||||
client.emit = _.once(callback)
|
||||
})
|
||||
}
|
||||
|
||||
this.httpSrv.listen(PORT, done)
|
||||
})
|
||||
|
||||
afterEach(function (done) {
|
||||
this.server.close()
|
||||
|
||||
return this.httpSrv.close(() => {
|
||||
return done()
|
||||
this.httpSrv.close(() => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -145,38 +167,115 @@ describe('app/background', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('onChanged', () => {
|
||||
it('does not emit when cause is overwrite', (done) => {
|
||||
context('cookies', () => {
|
||||
it('onChanged does not emit when cause is overwrite', function (done) {
|
||||
const addListener = sinon.stub(browser.cookies.onChanged, 'addListener')
|
||||
const client = background.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
sinon.spy(client, 'emit')
|
||||
this.onConnect((client) => {
|
||||
sinon.spy(client, 'emit')
|
||||
|
||||
return client.on('connect', _.once(() => {
|
||||
const fn = addListener.getCall(0).args[0]
|
||||
|
||||
fn({ cause: 'overwrite' })
|
||||
|
||||
expect(client.emit).not.to.be.calledWith('automation:push:request')
|
||||
|
||||
return done()
|
||||
}))
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('emits \'automation:push:request\'', (done) => {
|
||||
it('onChanged emits automation:push:request change:cookie', function (done) {
|
||||
const info = { cause: 'explicit', cookie: { name: 'foo', value: 'bar' } }
|
||||
|
||||
sinon.stub(browser.cookies.onChanged, 'addListener').yieldsAsync(info)
|
||||
const client = background.connect(`http://localhost:${PORT}`, '/__socket.io')
|
||||
|
||||
return client.on('connect', () => {
|
||||
return client.emit = _.once((req, msg, data) => {
|
||||
expect(req).to.eq('automation:push:request')
|
||||
expect(msg).to.eq('change:cookie')
|
||||
expect(data).to.deep.eq(info)
|
||||
this.stubEmit((req, msg, data) => {
|
||||
expect(req).to.eq('automation:push:request')
|
||||
expect(msg).to.eq('change:cookie')
|
||||
expect(data).to.deep.eq(info)
|
||||
|
||||
return done()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('downloads', () => {
|
||||
it('onCreated emits automation:push:request create:download', function (done) {
|
||||
const downloadItem = {
|
||||
id: '1',
|
||||
filename: '/path/to/download.csv',
|
||||
mime: 'text/csv',
|
||||
url: 'http://localhost:1234/download.csv',
|
||||
}
|
||||
|
||||
sinon.stub(browser.downloads.onCreated, 'addListener').yieldsAsync(downloadItem)
|
||||
|
||||
this.stubEmit((req, msg, data) => {
|
||||
expect(req).to.eq('automation:push:request')
|
||||
expect(msg).to.eq('create:download')
|
||||
expect(data).to.deep.eq({
|
||||
id: `${downloadItem.id}`,
|
||||
filePath: downloadItem.filename,
|
||||
mime: downloadItem.mime,
|
||||
url: downloadItem.url,
|
||||
})
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged emits automation:push:request complete:download', function (done) {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'complete',
|
||||
},
|
||||
}
|
||||
|
||||
sinon.stub(browser.downloads.onChanged, 'addListener').yieldsAsync(downloadDelta)
|
||||
|
||||
this.stubEmit((req, msg, data) => {
|
||||
expect(req).to.eq('automation:push:request')
|
||||
expect(msg).to.eq('complete:download')
|
||||
expect(data).to.deep.eq({ id: `${downloadDelta.id}` })
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state does not exist', function (done) {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
}
|
||||
const addListener = sinon.stub(browser.downloads.onChanged, 'addListener')
|
||||
|
||||
this.onConnect((client) => {
|
||||
sinon.spy(client, 'emit')
|
||||
addListener.getCall(0).args[0](downloadDelta)
|
||||
|
||||
expect(client.emit).not.to.be.calledWith('automation:push:request')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('onChanged does not emit if state.current is not "complete"', function (done) {
|
||||
const downloadDelta = {
|
||||
id: '1',
|
||||
state: {
|
||||
current: 'inprogress',
|
||||
},
|
||||
}
|
||||
const addListener = sinon.stub(browser.downloads.onChanged, 'addListener')
|
||||
|
||||
this.onConnect((client) => {
|
||||
sinon.spy(client, 'emit')
|
||||
|
||||
addListener.getCall(0).args[0](downloadDelta)
|
||||
|
||||
expect(client.emit).not.to.be.calledWith('automation:push:request')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -398,7 +398,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.command-name-log, .command-name-get {
|
||||
.command-name-log,
|
||||
.command-name-get,
|
||||
.command-name-download {
|
||||
// we're wrapping the text, so override command-scaled
|
||||
font-size: 100%;
|
||||
line-height: 18px;
|
||||
|
||||
.command-message-text {
|
||||
white-space: initial;
|
||||
word-wrap: break-word;
|
||||
|
||||
@@ -83,6 +83,12 @@ const eventManager = {
|
||||
case 'change:cookie':
|
||||
Cypress.Cookies.log(data.message, data.cookie, data.removed)
|
||||
break
|
||||
case 'create:download':
|
||||
Cypress.downloads.start(data)
|
||||
break
|
||||
case 'complete:download':
|
||||
Cypress.downloads.end(data)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ module.exports = {
|
||||
return cookies.clearCookie(data, automate)
|
||||
case 'change:cookie':
|
||||
return cookies.changeCookie(data)
|
||||
case 'create:download':
|
||||
case 'complete:download':
|
||||
return data
|
||||
default:
|
||||
return automate(data)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import _ from 'lodash'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import extension from '@packages/extension'
|
||||
import mime from 'mime'
|
||||
|
||||
import appData from '../util/app_data'
|
||||
import fs from '../util/fs'
|
||||
import { CdpAutomation } from './cdp_automation'
|
||||
@@ -322,8 +324,36 @@ const _navigateUsingCRI = async function (client, url) {
|
||||
await client.send('Page.navigate', { url })
|
||||
}
|
||||
|
||||
const _setDownloadsDir = async function (client, dir) {
|
||||
await client.send('Page.setDownloadBehavior', {
|
||||
const _handleDownloads = async function (client, dir, automation) {
|
||||
await client.send('Page.enable')
|
||||
|
||||
client.on('Page.downloadWillBegin', (data) => {
|
||||
const downloadItem = {
|
||||
id: data.guid,
|
||||
url: data.url,
|
||||
}
|
||||
|
||||
const filename = data.suggestedFilename
|
||||
|
||||
if (filename) {
|
||||
// @ts-ignore
|
||||
downloadItem.filePath = path.join(dir, data.suggestedFilename)
|
||||
// @ts-ignore
|
||||
downloadItem.mime = mime.getType(data.suggestedFilename)
|
||||
}
|
||||
|
||||
automation.push('create:download', downloadItem)
|
||||
})
|
||||
|
||||
client.on('Page.downloadProgress', (data) => {
|
||||
if (data.state !== 'completed') return
|
||||
|
||||
automation.push('complete:download', {
|
||||
id: data.guid,
|
||||
})
|
||||
})
|
||||
|
||||
await client.send('Browser.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: dir,
|
||||
})
|
||||
@@ -352,7 +382,7 @@ export = {
|
||||
|
||||
_navigateUsingCRI,
|
||||
|
||||
_setDownloadsDir,
|
||||
_handleDownloads,
|
||||
|
||||
_setAutomation,
|
||||
|
||||
@@ -528,7 +558,7 @@ export = {
|
||||
|
||||
await this._maybeRecordVideo(criClient, options)
|
||||
await this._navigateUsingCRI(criClient, url)
|
||||
await this._setDownloadsDir(criClient, options.downloadsFolder)
|
||||
await this._handleDownloads(criClient, options.downloadsFolder, automation)
|
||||
|
||||
// return the launched browser process
|
||||
// with additional method to close the remote connection
|
||||
|
||||
@@ -30,10 +30,13 @@ namespace CRI {
|
||||
'Page.captureScreenshot' |
|
||||
'Page.navigate' |
|
||||
'Page.startScreencast' |
|
||||
'Page.screencastFrameAck'
|
||||
'Page.screencastFrameAck' |
|
||||
'Browser.setDownloadBehavior'
|
||||
|
||||
export type EventName =
|
||||
'Page.screencastFrame'
|
||||
'Page.screencastFrame' |
|
||||
'Page.downloadWillBegin' |
|
||||
'Page.downloadProgress'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const _ = require('lodash')
|
||||
const EE = require('events')
|
||||
const path = require('path')
|
||||
const Bluebird = require('bluebird')
|
||||
const debug = require('debug')('cypress:server:browsers:electron')
|
||||
const menu = require('../gui/menu')
|
||||
@@ -69,11 +70,11 @@ const _getAutomation = function (win, options) {
|
||||
const _installExtensions = function (win, extensionPaths = [], options) {
|
||||
Windows.removeAllExtensions(win)
|
||||
|
||||
return Bluebird.map(extensionPaths, (path) => {
|
||||
return Bluebird.map(extensionPaths, (extensionPath) => {
|
||||
try {
|
||||
return Windows.installExtension(win, path)
|
||||
return Windows.installExtension(win, extensionPath)
|
||||
} catch (error) {
|
||||
return options.onWarning(errors.get('EXTENSION_NOT_LOADED', 'Electron', path))
|
||||
return options.onWarning(errors.get('EXTENSION_NOT_LOADED', 'Electron', extensionPath))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -162,7 +163,7 @@ module.exports = {
|
||||
|
||||
automation.use(_getAutomation(win, options))
|
||||
|
||||
return this._launch(win, url, options)
|
||||
return this._launch(win, url, automation, options)
|
||||
.tap(_maybeRecordVideo(win.webContents, options))
|
||||
},
|
||||
|
||||
@@ -188,7 +189,7 @@ module.exports = {
|
||||
return this._launch(win, url, options)
|
||||
},
|
||||
|
||||
_launch (win, url, options) {
|
||||
_launch (win, url, automation, options) {
|
||||
if (options.show) {
|
||||
menu.set({ withDevTools: true })
|
||||
}
|
||||
@@ -234,7 +235,7 @@ module.exports = {
|
||||
return this._enableDebugger(win.webContents)
|
||||
})
|
||||
.then(() => {
|
||||
return this._setDownloadsDir(win.webContents, options.downloadsFolder)
|
||||
return this._handleDownloads(win.webContents, options.downloadsFolder, automation)
|
||||
})
|
||||
.return(win)
|
||||
},
|
||||
@@ -290,7 +291,24 @@ module.exports = {
|
||||
return webContents.debugger.sendCommand('Console.enable')
|
||||
},
|
||||
|
||||
_setDownloadsDir (webContents, dir) {
|
||||
_handleDownloads (webContents, dir, automation) {
|
||||
webContents.session.on('will-download', (event, downloadItem) => {
|
||||
const savePath = path.join(dir, downloadItem.getFilename())
|
||||
|
||||
automation.push('create:download', {
|
||||
id: downloadItem.getETag(),
|
||||
filePath: savePath,
|
||||
mime: downloadItem.getMimeType(),
|
||||
url: downloadItem.getURL(),
|
||||
})
|
||||
|
||||
downloadItem.once('done', () => {
|
||||
automation.push('complete:download', {
|
||||
id: downloadItem.getETag(),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return webContents.debugger.sendCommand('Page.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: dir,
|
||||
|
||||
@@ -1126,6 +1126,7 @@ describe('lib/cypress', () => {
|
||||
clearCache: sinon.stub().resolves(),
|
||||
setProxy: sinon.stub().resolves(),
|
||||
setUserAgent: sinon.stub(),
|
||||
on: sinon.stub(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1147,7 +1148,7 @@ describe('lib/cypress', () => {
|
||||
// it accepts URL to visit and then waits for actual CRI client reference
|
||||
// and only then navigates to that URL
|
||||
sinon.stub(chromeBrowser, '_navigateUsingCRI').resolves()
|
||||
sinon.stub(chromeBrowser, '_setDownloadsDir').resolves()
|
||||
sinon.stub(chromeBrowser, '_handleDownloads').resolves()
|
||||
|
||||
sinon.stub(chromeBrowser, '_setAutomation').returns()
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ describe('lib/browsers/chrome', () => {
|
||||
screencastFrame: sinon.stub().returns(),
|
||||
},
|
||||
close: sinon.stub().resolves(),
|
||||
on: sinon.stub(),
|
||||
}
|
||||
|
||||
this.automation = {
|
||||
push: sinon.stub(),
|
||||
use: sinon.stub().returns(),
|
||||
}
|
||||
|
||||
@@ -30,6 +32,15 @@ describe('lib/browsers/chrome', () => {
|
||||
kill: sinon.stub().returns(),
|
||||
}
|
||||
|
||||
this.onCriEvent = (event, data, options) => {
|
||||
this.criClient.on.withArgs(event).yieldsAsync(data)
|
||||
|
||||
return chrome.open('chrome', 'http://', options, this.automation)
|
||||
.then(() => {
|
||||
this.criClient.on = undefined
|
||||
})
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_writeExtension').resolves('/path/to/ext')
|
||||
sinon.stub(chrome, '_connectToChromeRemoteInterface').resolves(this.criClient)
|
||||
sinon.stub(plugins, 'execute').callThrough()
|
||||
@@ -43,22 +54,23 @@ describe('lib/browsers/chrome', () => {
|
||||
this.readJson.withArgs('/profile/dir/Local State').rejects({ code: 'ENOENT' })
|
||||
|
||||
// port for Chrome remote interface communication
|
||||
return sinon.stub(utils, 'getPort').resolves(50505)
|
||||
sinon.stub(utils, 'getPort').resolves(50505)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
expect(this.criClient.ensureMinimumProtocolVersion).to.be.calledOnce
|
||||
})
|
||||
|
||||
it('focuses on the page, calls CRI Page.visit, and sets download behavior', function () {
|
||||
it('focuses on the page, calls CRI Page.visit, enables Page events, and sets download behavior', function () {
|
||||
return chrome.open('chrome', 'http://', {}, this.automation)
|
||||
.then(() => {
|
||||
expect(utils.getPort).to.have.been.calledOnce // to get remote interface port
|
||||
expect(this.criClient.send).to.have.been.calledThrice
|
||||
expect(this.criClient.send.callCount).to.equal(4)
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.bringToFront')
|
||||
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.navigate')
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.setDownloadBehavior')
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.enable')
|
||||
expect(this.criClient.send).to.have.been.calledWith('Browser.setDownloadBehavior')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -261,23 +273,50 @@ describe('lib/browsers/chrome', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/9265
|
||||
it('respond ACK after receiving new screenshot frame', function () {
|
||||
const frameMeta = { data: Buffer.from(''), sessionId: '1' }
|
||||
|
||||
this.criClient.on = (eventName, fn) => {
|
||||
if (eventName === 'Page.screencastFrame') {
|
||||
fn(frameMeta)
|
||||
}
|
||||
}
|
||||
|
||||
const write = sinon.stub()
|
||||
const options = { onScreencastFrame: write }
|
||||
|
||||
return chrome.open('chrome', 'http://', { onScreencastFrame: write }, this.automation)
|
||||
return this.onCriEvent('Page.screencastFrame', frameMeta, options)
|
||||
.then(() => {
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.startScreencast')
|
||||
expect(write).to.have.been.calledWith(frameMeta)
|
||||
expect(this.criClient.send).to.have.been.calledWith('Page.screencastFrameAck', { sessionId: frameMeta.sessionId })
|
||||
})
|
||||
.then(() => {
|
||||
this.criClient.on = undefined
|
||||
})
|
||||
|
||||
describe('downloads', function () {
|
||||
it('pushes create:download after download begins', function () {
|
||||
const downloadData = {
|
||||
guid: '1',
|
||||
suggestedFilename: 'file.csv',
|
||||
url: 'http://localhost:1234/file.csv',
|
||||
}
|
||||
const options = { downloadsFolder: 'downloads' }
|
||||
|
||||
return this.onCriEvent('Page.downloadWillBegin', downloadData, options)
|
||||
.then(() => {
|
||||
expect(this.automation.push).to.be.calledWith('create:download', {
|
||||
id: '1',
|
||||
filePath: 'downloads/file.csv',
|
||||
mime: 'text/csv',
|
||||
url: 'http://localhost:1234/file.csv',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('pushes complete:download after download completes', function () {
|
||||
const downloadData = {
|
||||
guid: '1',
|
||||
state: 'completed',
|
||||
}
|
||||
const options = { downloadsFolder: 'downloads' }
|
||||
|
||||
return this.onCriEvent('Page.downloadProgress', downloadData, options)
|
||||
.then(() => {
|
||||
expect(this.automation.push).to.be.calledWith('complete:download', {
|
||||
id: '1',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('lib/browsers/electron', () => {
|
||||
set: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
},
|
||||
on: sinon.stub(),
|
||||
},
|
||||
getOSProcessId: sinon.stub().returns(ELECTRON_PID),
|
||||
'debugger': {
|
||||
@@ -165,58 +166,114 @@ describe('lib/browsers/electron', () => {
|
||||
sinon.stub(electron, '_attachDebugger').resolves()
|
||||
sinon.stub(electron, '_clearCache').resolves()
|
||||
sinon.stub(electron, '_setProxy').resolves()
|
||||
|
||||
return sinon.stub(electron, '_setUserAgent')
|
||||
sinon.stub(electron, '_setUserAgent')
|
||||
})
|
||||
|
||||
it('sets menu.set whether or not its in headless mode', function () {
|
||||
return electron._launch(this.win, this.url, { show: true })
|
||||
return electron._launch(this.win, this.url, this.automation, { show: true })
|
||||
.then(() => {
|
||||
expect(menu.set).to.be.calledWith({ withDevTools: true })
|
||||
}).then(() => {
|
||||
menu.set.reset()
|
||||
|
||||
return electron._launch(this.win, this.url, { show: false })
|
||||
return electron._launch(this.win, this.url, this.automation, { show: false })
|
||||
}).then(() => {
|
||||
expect(menu.set).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('sets user agent if options.userAgent', function () {
|
||||
return electron._launch(this.win, this.url, this.options)
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(electron._setUserAgent).not.to.be.called
|
||||
}).then(() => {
|
||||
return electron._launch(this.win, this.url, { userAgent: 'foo' })
|
||||
return electron._launch(this.win, this.url, this.automation, { userAgent: 'foo' })
|
||||
}).then(() => {
|
||||
expect(electron._setUserAgent).to.be.calledWith(this.win.webContents, 'foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets proxy if options.proxyServer', function () {
|
||||
return electron._launch(this.win, this.url, this.options)
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(electron._setProxy).not.to.be.called
|
||||
}).then(() => {
|
||||
return electron._launch(this.win, this.url, { proxyServer: 'foo' })
|
||||
return electron._launch(this.win, this.url, this.automation, { proxyServer: 'foo' })
|
||||
}).then(() => {
|
||||
expect(electron._setProxy).to.be.calledWith(this.win.webContents, 'foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('calls win.loadURL with url', function () {
|
||||
return electron._launch(this.win, this.url, this.options)
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(this.win.loadURL).to.be.calledWith(this.url)
|
||||
})
|
||||
})
|
||||
|
||||
it('resolves with win', function () {
|
||||
return electron._launch(this.win, this.url, this.options)
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then((win) => {
|
||||
expect(win).to.eq(this.win)
|
||||
})
|
||||
})
|
||||
|
||||
it('pushes create:download when download begins', function () {
|
||||
const downloadItem = {
|
||||
getETag: () => '1',
|
||||
getFilename: () => 'file.csv',
|
||||
getMimeType: () => 'text/csv',
|
||||
getURL: () => 'http://localhost:1234/file.csv',
|
||||
once: sinon.stub(),
|
||||
}
|
||||
|
||||
this.win.webContents.session.on.withArgs('will-download').yields({}, downloadItem)
|
||||
this.options.downloadsFolder = 'downloads'
|
||||
sinon.stub(this.automation, 'push')
|
||||
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(this.automation.push).to.be.calledWith('create:download', {
|
||||
id: '1',
|
||||
filePath: 'downloads/file.csv',
|
||||
mime: 'text/csv',
|
||||
url: 'http://localhost:1234/file.csv',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('pushes complete:download when download is done', function () {
|
||||
const downloadItem = {
|
||||
getETag: () => '1',
|
||||
getFilename: () => 'file.csv',
|
||||
getMimeType: () => 'text/csv',
|
||||
getURL: () => 'http://localhost:1234/file.csv',
|
||||
once: sinon.stub().yields(),
|
||||
}
|
||||
|
||||
this.win.webContents.session.on.withArgs('will-download').yields({}, downloadItem)
|
||||
this.options.downloadsFolder = 'downloads'
|
||||
sinon.stub(this.automation, 'push')
|
||||
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(this.automation.push).to.be.calledWith('complete:download', {
|
||||
id: '1',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('sets download behavior', function () {
|
||||
this.options.downloadsFolder = 'downloads'
|
||||
|
||||
return electron._launch(this.win, this.url, this.automation, this.options)
|
||||
.then(() => {
|
||||
expect(this.win.webContents.debugger.sendCommand).to.be.calledWith('Page.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: 'downloads',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('._render', () => {
|
||||
@@ -237,7 +294,7 @@ describe('lib/browsers/electron', () => {
|
||||
.then(() => {
|
||||
expect(Windows.create).to.be.calledWith(this.options.projectRoot, this.options)
|
||||
|
||||
expect(electron._launch).to.be.calledWith(this.newWin, this.url, this.options)
|
||||
expect(electron._launch).to.be.calledWith(this.newWin, this.url, this.automation, this.options)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -112,6 +112,14 @@ describe('lib/socket', () => {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
downloads: {
|
||||
onCreated: {
|
||||
addListener () {},
|
||||
},
|
||||
onChanged: {
|
||||
addListener () {},
|
||||
},
|
||||
},
|
||||
runtime: {
|
||||
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user