Merge pull request #7345 from sainthkh/decaff-driver-7

This commit is contained in:
Jennifer Shehane
2020-05-18 12:38:16 +06:30
committed by GitHub
22 changed files with 5765 additions and 4468 deletions

View File

@@ -53,7 +53,7 @@ describe('src/cy/commands/actions/check', () => {
})
it('changes the subject when matching values even if noop', () => {
const checked = $('<input type=\'checkbox\' name=\'colors\' value=\'blue\' checked>')
const checked = $(`<input type='checkbox' name='colors' value='blue' checked>`)
$('[name=colors]').parent().append(checked)

View File

@@ -553,16 +553,12 @@ describe('src/cy/commands/agents', () => {
it('does not include stack with calls when assertion fails', function (done) {
cy.on('fail', () => {
const messages = [
expect(this.lastLog.get('message')).to.include([
' foo("str", 5, true) => "return value"',
' foo(null, undefined, [1, 2, 3]) => "return value"',
' foo({g: 1}, Array[5], Object{6}) => "return value"',
' foo(1, 2, 3, 4, 5) => "return value"',
]
messages.forEach((msg) => {
expect(this.lastLog.get('message')).to.include(msg)
})
].join('\n'))
done()
})

View File

@@ -1,321 +0,0 @@
describe "src/cy/commands/clock", ->
beforeEach ->
@window = cy.state("window")
@setTimeoutSpy = cy.spy(@window, "setTimeout")
@setIntervalSpy = cy.spy(@window, "setInterval")
describe "#clock", ->
it "sets clock as subject", ->
cy.clock().then (clock) ->
expect(clock).to.exist
expect(clock.tick).to.be.a("function")
it "assigns clock to test context", ->
cy.clock().then (clock) ->
expect(clock).to.eq(@clock)
it "proxies lolex clock, replacing window time methods", (done) ->
expect(@setTimeoutSpy).not.to.be.called
cy.clock().then (clock) ->
## lolex calls setTimeout once as part of its setup
## but it shouldn't be called again by the @window.setTimeout()
expect(@setTimeoutSpy).to.be.calledOnce
@window.setTimeout =>
expect(@setTimeoutSpy).to.be.calledOnce
done()
clock.tick()
it "takes now arg", ->
now = 1111111111111
cy.clock(now).then (clock) ->
expect(new @window.Date().getTime()).to.equal(now)
clock.tick(4321)
expect(new @window.Date().getTime()).to.equal(now + 4321)
it "restores window time methods when calling restore", (done) ->
cy.clock().then (clock) ->
@window.setTimeout =>
expect(@setTimeoutSpy).to.be.calledOnce
clock.restore()
expect(@window.setTimeout).to.equal(@setTimeoutSpy)
@window.setTimeout =>
expect(@setTimeoutSpy).to.be.calledTwice
done()
clock.tick()
it "unsets clock after restore", ->
cy.clock().then (clock) ->
expect(cy.state("clock")).to.exist
clock.restore()
expect(cy.state("clock")).to.be.null
expect(@clock).to.be.null
it "automatically restores clock on 'restore' event", ->
cy.clock().then (clock) ->
r = cy.spy(clock, "restore")
Cypress.emit("test:before:run", {})
expect(r).to.be.called
it "returns clock on subsequent calls, ignoring arguments", ->
cy
.clock()
.clock(400)
.then (clock) ->
expect(clock.details().now).to.equal(0)
it "new Date() is an instance of Date", ->
cy.clock()
cy.window().then (win) ->
expect(new win.Date()).to.be.an.instanceof(win.Date)
expect(new win.Date() instanceof win.Date).to.be.true
## this test was written to catch a bug in lolex (dep) 3 and is fixed by lolex 4 upgrade,
it "doesn't override window.performance members", ->
cy.clock()
.then (clock) ->
cy.window().then (win) ->
expect(win.performance.getEntries()).to.deep.eq([])
clock.restore()
expect(win.performance.getEntries().length).to.be.at.least(1)
context "errors", ->
it "throws if now is not a number (or options object)", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.equal("`cy.clock()` only accepts a number or an `options` object for its first argument. You passed: `\"250\"`")
expect(err.docsUrl).to.equal("https://on.cypress.io/clock")
done()
cy.clock("250")
it "throws if methods is not an array (or options object)", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.equal("`cy.clock()` only accepts an array of function names or an `options` object for its second argument. You passed: `\"setTimeout\"`")
expect(err.docsUrl).to.equal("https://on.cypress.io/clock")
done()
cy.clock(0, "setTimeout")
it "throws if methods is not an array of strings (or options object)", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.equal("`cy.clock()` only accepts an array of function names or an `options` object for its second argument. You passed: `[42]`")
expect(err.docsUrl).to.equal("https://on.cypress.io/clock")
done()
cy.clock(0, [42])
context "arg for which functions to replace", ->
it "replaces specified functions", (done) ->
cy.clock(null, ["setTimeout"]).then (clock) ->
@window.setTimeout =>
expect(@setTimeoutSpy).to.be.calledOnce
done()
clock.tick()
it "does not replace other functions", (done) ->
cy.clock(null, ["setTimeout"]).then (clock) =>
interval = @window.setInterval =>
@window.clearInterval(interval)
expect(@setIntervalSpy).to.be.called
@window.setTimeout =>
expect(@setTimeoutSpy).to.be.calledOnce
done()
clock.tick()
, 5
context "options", ->
beforeEach ->
@logged = false
cy.on "log:added", (attrs, log) =>
if log.get("name") is "clock"
@logged = true
return null
it "can be first arg", ->
cy.clock({log: false}).then =>
expect(@logged).to.be.false
it "can be second arg", ->
cy.clock(new Date().getTime(), {log: false}).then =>
expect(@logged).to.be.false
it "can be third arg", ->
cy.clock(new Date().getTime(), ["setTimeout"], {log: false}).then =>
expect(@logged).to.be.false
context "window changes", ->
it "binds to default window before visit", ->
cy.clock(null, ["setTimeout"]).then (clock) =>
onSetTimeout = cy.spy()
cy.state("window").setTimeout(onSetTimeout)
clock.tick()
expect(onSetTimeout).to.be.called
it "re-binds to new window when window changes", ->
newWindow = {
setTimeout: ->
clearTimeout: ->
Date: ->
XMLHttpRequest: {
prototype: {}
}
}
cy.clock(null, ["setTimeout"]).then (clock) =>
Cypress.emit("window:before:load", newWindow)
onSetTimeout = cy.spy()
newWindow.setTimeout(onSetTimeout)
clock.tick()
expect(onSetTimeout).to.be.called
it "binds to window if called before visit", ->
cy.clock()
cy.visit('/fixtures/dom.html') ## should not throw
context "logging", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
name = log.get("name")
if name in ["clock", "tick", "restore"]
@logs.push(log)
return null
it "logs when created", ->
cy.clock().then =>
log = @logs[0]
expect(@logs.length).to.equal(1)
expect(log.get("name")).to.eq("clock")
expect(log.get("message")).to.eq("")
expect(log.get("type")).to.eq("parent")
expect(log.get("state")).to.eq("passed")
expect(log.get("snapshots").length).to.eq(1)
expect(log.get("snapshots")[0]).to.be.an("object")
it "logs when restored", ->
cy.clock().then (clock) =>
clock.restore()
log = @logs[1]
expect(@logs.length).to.equal(2)
expect(log.get("name")).to.eq("restore")
expect(log.get("message")).to.eq("")
it "does not log when auto-restored", (done) ->
cy.clock().then =>
Cypress.emit("test:before:run", {})
expect(@logs.length).to.equal(1)
done()
it "does not log when log: false", ->
cy.clock({log: false}).then (clock) =>
clock.tick()
clock.restore()
expect(@logs.length).to.equal(0)
it "only logs the first call", ->
cy
.clock()
.clock()
.clock()
.then =>
expect(@logs.length).to.equal(1)
context "#consoleProps", ->
beforeEach ->
cy.clock(100, ["setTimeout"]).then (@clock) ->
@clock.tick(100)
it "includes clock's now value", ->
consoleProps = @logs[0].invoke("consoleProps")
expect(consoleProps["Now"]).to.equal(100)
it "includes methods replaced by clock", ->
consoleProps = @logs[0].invoke("consoleProps")
expect(consoleProps["Methods replaced"]).to.eql(["setTimeout"])
it "logs ticked amount on tick", ->
createdConsoleProps = @logs[0].invoke("consoleProps")
expect(createdConsoleProps["Ticked"]).to.be.undefined
tickedConsoleProps = @logs[1].invoke("consoleProps")
expect(tickedConsoleProps["Ticked"]).to.equal("100 milliseconds")
it "properties are unaffected by future actions", ->
@clock.tick(100)
@clock.restore()
consoleProps = @logs[1].invoke("consoleProps")
expect(consoleProps["Now"]).to.equal(200)
expect(consoleProps["Methods replaced"]).to.eql(["setTimeout"])
describe "#tick", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
if log.get("name") is "tick"
@logs.push(log)
return null
it "moves time ahead and triggers callbacks", (done) ->
cy
.clock()
.then =>
@window.setTimeout ->
done()
, 1000
.tick(1000)
it "returns the clock object", ->
cy
.clock()
.tick(1000).then (clock) ->
expect(clock).to.equal(@clock)
it "defaults to 0ms", ->
cy.clock()
.tick().then (clock) ->
consoleProps = @logs[0].invoke("consoleProps")
expect(consoleProps["Ticked"]).to.equal("0 milliseconds")
context "errors", ->
it "throws if there is not a clock", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.equal("`cy.tick()` cannot be called without first calling `cy.clock()`")
expect(err.docsUrl).to.equal('https://on.cypress.io/tick')
done()
cy.tick()
it "throws if ms is not undefined or a number", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.equal("`clock.tick()`/`cy.tick()` only accepts a number as their argument. You passed: `\"100\"`")
expect(err.docsUrl).to.equal('https://on.cypress.io/tick')
done()
cy.clock().tick("100")
context "logging", ->
it "logs number of milliseconds", ->
cy
.clock()
.tick(250)
.then ->
log = @logs[0]
expect(@logs.length).to.equal(1)
expect(log.get("name")).to.eq("tick")
expect(log.get("message")).to.eq("250ms")
it "logs before and after snapshots", ->
cy
.clock()
.tick(250)
.then ->
log = @logs[0]
expect(log.get("snapshots").length).to.eq(2)
expect(log.get("snapshots")[0].name).to.equal("before")
expect(log.get("snapshots")[1].name).to.equal("after")

View File

@@ -0,0 +1,454 @@
describe('src/cy/commands/clock', () => {
beforeEach(function () {
this.window = cy.state('window')
this.setTimeoutSpy = cy.spy(this.window, 'setTimeout')
this.setIntervalSpy = cy.spy(this.window, 'setInterval')
})
describe('#clock', () => {
it('sets clock as subject', () => {
cy.clock().then((clock) => {
expect(clock).to.exist
expect(clock.tick).to.be.a('function')
})
})
it('assigns clock to test context', () => {
cy.clock().then(function (clock) {
expect(clock).to.eq(this.clock)
})
})
it('proxies lolex clock, replacing window time methods', function (done) {
expect(this.setTimeoutSpy).not.to.be.called
cy.clock().then(function (clock) {
// lolex calls setTimeout once as part of its setup
// but it shouldn't be called again by the @window.setTimeout()
expect(this.setTimeoutSpy).to.be.calledOnce
this.window.setTimeout(() => {
expect(this.setTimeoutSpy).to.be.calledOnce
done()
})
clock.tick()
})
})
it('takes now arg', () => {
const now = 1111111111111
cy.clock(now).then(function (clock) {
expect(new this.window.Date().getTime()).to.equal(now)
clock.tick(4321)
expect(new this.window.Date().getTime()).to.equal(now + 4321)
})
})
it('restores window time methods when calling restore', (done) => {
cy.clock().then(function (clock) {
this.window.setTimeout(() => {
expect(this.setTimeoutSpy).to.be.calledOnce
clock.restore()
expect(this.window.setTimeout).to.equal(this.setTimeoutSpy)
this.window.setTimeout(() => {
expect(this.setTimeoutSpy).to.be.calledTwice
done()
})
})
clock.tick()
})
})
it('unsets clock after restore', () => {
cy.clock().then(function (clock) {
expect(cy.state('clock')).to.exist
clock.restore()
expect(cy.state('clock')).to.be.null
expect(this.clock).to.be.null
})
})
it('automatically restores clock on \'restore\' event', () => {
cy.clock().then((clock) => {
const r = cy.spy(clock, 'restore')
Cypress.emit('test:before:run', {})
expect(r).to.be.called
})
})
it('returns clock on subsequent calls, ignoring arguments', () => {
cy
.clock()
.clock(400)
.then((clock) => {
expect(clock.details().now).to.equal(0)
})
})
it('new Date() is an instance of Date', () => {
cy.clock()
cy.window().then((win) => {
expect(new win.Date()).to.be.an.instanceof(win.Date)
expect(new win.Date() instanceof win.Date).to.be.true
})
})
// this test was written to catch a bug in lolex (dep) 3 and is fixed by lolex 4 upgrade,
it('doesn\'t override window.performance members', () => {
cy.clock()
.then((clock) => {
cy.window().then((win) => {
expect(win.performance.getEntries()).to.deep.eq([])
clock.restore()
expect(win.performance.getEntries().length).to.be.at.least(1)
})
})
})
context('errors', () => {
it('throws if now is not a number (or options object)', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.clock()` only accepts a number or an `options` object for its first argument. You passed: `"250"`')
expect(err.docsUrl).to.equal('https://on.cypress.io/clock')
done()
})
cy.clock('250')
})
it('throws if methods is not an array (or options object)', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.clock()` only accepts an array of function names or an `options` object for its second argument. You passed: `"setTimeout"`')
expect(err.docsUrl).to.equal('https://on.cypress.io/clock')
done()
})
cy.clock(0, 'setTimeout')
})
it('throws if methods is not an array of strings (or options object)', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.clock()` only accepts an array of function names or an `options` object for its second argument. You passed: `[42]`')
expect(err.docsUrl).to.equal('https://on.cypress.io/clock')
done()
})
cy.clock(0, [42])
})
})
context('arg for which functions to replace', () => {
it('replaces specified functions', (done) => {
cy.clock(null, ['setTimeout']).then(function (clock) {
this.window.setTimeout(() => {
expect(this.setTimeoutSpy).to.be.calledOnce
done()
})
clock.tick()
})
})
it('does not replace other functions', function (done) {
cy.clock(null, ['setTimeout']).then((clock) => {
const interval = this.window.setInterval(() => {
this.window.clearInterval(interval)
expect(this.setIntervalSpy).to.be.called
this.window.setTimeout(() => {
expect(this.setTimeoutSpy).to.be.calledOnce
done()
})
clock.tick()
}, 5)
})
})
})
context('options', () => {
beforeEach(function () {
this.logged = false
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'clock') {
this.logged = true
}
})
return null
})
it('can be first arg', function () {
cy.clock({ log: false }).then(() => {
expect(this.logged).to.be.false
})
})
it('can be second arg', function () {
cy.clock(new Date().getTime(), { log: false }).then(() => {
expect(this.logged).to.be.false
})
})
it('can be third arg', function () {
cy.clock(new Date().getTime(), ['setTimeout'], { log: false }).then(() => {
expect(this.logged).to.be.false
})
})
})
context('window changes', () => {
it('binds to default window before visit', () => {
cy.clock(null, ['setTimeout']).then((clock) => {
const onSetTimeout = cy.spy()
cy.state('window').setTimeout(onSetTimeout)
clock.tick()
expect(onSetTimeout).to.be.called
})
})
it('re-binds to new window when window changes', () => {
const newWindow = {
setTimeout () {},
clearTimeout () {},
Date () {},
XMLHttpRequest: {
prototype: {},
},
}
cy.clock(null, ['setTimeout']).then((clock) => {
Cypress.emit('window:before:load', newWindow)
const onSetTimeout = cy.spy()
newWindow.setTimeout(onSetTimeout)
clock.tick()
expect(onSetTimeout).to.be.called
})
})
it('binds to window if called before visit', () => {
cy.clock()
cy.visit('/fixtures/dom.html')// should not throw
})
})
context('logging', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
const name = log.get('name')
if (['clock', 'tick', 'restore'].includes(name)) {
return this.logs.push(log)
}
})
return null
})
it('logs when created', function () {
cy.clock().then(() => {
const log = this.logs[0]
expect(this.logs.length).to.equal(1)
expect(log.get('name')).to.eq('clock')
expect(log.get('message')).to.eq('')
expect(log.get('type')).to.eq('parent')
expect(log.get('state')).to.eq('passed')
expect(log.get('snapshots').length).to.eq(1)
expect(log.get('snapshots')[0]).to.be.an('object')
})
})
it('logs when restored', function () {
cy.clock().then((clock) => {
clock.restore()
const log = this.logs[1]
expect(this.logs.length).to.equal(2)
expect(log.get('name')).to.eq('restore')
expect(log.get('message')).to.eq('')
})
})
it('does not log when auto-restored', function (done) {
cy.clock().then(() => {
Cypress.emit('test:before:run', {})
expect(this.logs.length).to.equal(1)
done()
})
})
it('does not log when log: false', function () {
cy.clock({ log: false }).then((clock) => {
clock.tick()
clock.restore()
expect(this.logs.length).to.equal(0)
})
})
it('only logs the first call', function () {
cy
.clock()
.clock()
.clock()
.then(() => {
expect(this.logs.length).to.equal(1)
})
})
context('#consoleProps', () => {
beforeEach(() => {
cy.clock(100, ['setTimeout']).then(function (clock) {
this.clock = clock
this.clock.tick(100)
})
})
it('includes clock\'s now value', function () {
const consoleProps = this.logs[0].invoke('consoleProps')
expect(consoleProps['Now']).to.equal(100)
})
it('includes methods replaced by clock', function () {
const consoleProps = this.logs[0].invoke('consoleProps')
expect(consoleProps['Methods replaced']).to.eql(['setTimeout'])
})
it('logs ticked amount on tick', function () {
const createdConsoleProps = this.logs[0].invoke('consoleProps')
expect(createdConsoleProps['Ticked']).to.be.undefined
const tickedConsoleProps = this.logs[1].invoke('consoleProps')
expect(tickedConsoleProps['Ticked']).to.equal('100 milliseconds')
})
it('properties are unaffected by future actions', function () {
this.clock.tick(100)
this.clock.restore()
const consoleProps = this.logs[1].invoke('consoleProps')
expect(consoleProps['Now']).to.equal(200)
expect(consoleProps['Methods replaced']).to.eql(['setTimeout'])
})
})
})
})
describe('#tick', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'tick') {
this.logs.push(log)
}
})
return null
})
it('moves time ahead and triggers callbacks', function (done) {
cy
.clock()
.then(() => {
return this.window.setTimeout(() => {
done()
}, 1000)
}).tick(1000)
})
it('returns the clock object', () => {
cy
.clock()
.tick(1000).then(function (clock) {
expect(clock).to.equal(this.clock)
})
})
it('defaults to 0ms', () => {
cy.clock()
.tick().then(function (clock) {
const consoleProps = this.logs[0].invoke('consoleProps')
expect(consoleProps['Ticked']).to.equal('0 milliseconds')
})
})
context('errors', () => {
it('throws if there is not a clock', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.tick()` cannot be called without first calling `cy.clock()`')
expect(err.docsUrl).to.equal('https://on.cypress.io/tick')
done()
})
cy.tick()
})
it('throws if ms is not undefined or a number', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`clock.tick()`/`cy.tick()` only accepts a number as their argument. You passed: `"100"`')
expect(err.docsUrl).to.equal('https://on.cypress.io/tick')
done()
})
cy.clock().tick('100')
})
})
context('logging', () => {
it('logs number of milliseconds', () => {
cy
.clock()
.tick(250)
.then(function () {
const log = this.logs[0]
expect(this.logs.length).to.equal(1)
expect(log.get('name')).to.eq('tick')
expect(log.get('message')).to.eq('250ms')
})
})
it('logs before and after snapshots', () => {
cy
.clock()
.tick(250)
.then(function () {
const log = this.logs[0]
expect(log.get('snapshots').length).to.eq(2)
expect(log.get('snapshots')[0].name).to.equal('before')
expect(log.get('snapshots')[1].name).to.equal('after')
})
})
})
})
})

View File

@@ -1,76 +0,0 @@
_ = Cypress._
$ = Cypress.$
describe "src/cy/commands/commands", ->
before ->
cy
.visit("/fixtures/dom.html")
.then (win) ->
@body = win.document.body.outerHTML
beforeEach ->
doc = cy.state("document")
$(doc.body).empty().html(@body)
it "can invoke commands by name", ->
body = cy.$$("body")
cy
.get("body").then ($body) ->
expect($body.get(0)).to.eq(body.get(0))
.command("get", "body").then ($body) ->
expect($body.get(0)).to.eq(body.get(0))
it "can invoke child commands by name", ->
div = cy.$$("body>div:first")
cy
.get("body").find("div:first").then ($div) ->
expect($div.get(0)).to.eq(div.get(0))
.get("body").command("find", "div:first").then ($div) ->
expect($div.get(0)).to.eq(div.get(0))
it "does not add cmds to cy.commands queue", ->
cy.command("get", "body").then ->
names = cy.queue.names()
expect(names).to.deep.eq(["get", "then"])
context "custom commands", ->
beforeEach ->
Cypress.Commands.add "dashboard.selectWindows", =>
cy
.get("[contenteditable]")
.first()
Cypress.Commands.add "login", { prevSubject: true }, (subject, email) =>
cy
.wrap(subject.find("input:first"))
.type(email)
it "works with custom commands", ->
input = cy.$$("input:first")
cy
.get("input:first")
.parent()
.command("login", "brian@foo.com").then ($input) ->
expect($input.get(0)).to.eq(input.get(0))
it "works with namespaced commands", ->
ce = cy.$$("[contenteditable]").first()
cy
.command("dashboard.selectWindows").then ($ce) ->
expect($ce.get(0)).to.eq(ce.get(0))
context "errors", ->
it "throws when cannot find command by name", (done) ->
cy.on "fail", (err) ->
cmds = _.keys(Cypress.Chainer.prototype)
expect(cmds.length).to.be.gt(1)
expect(err.message).to.eq("Could not find a command for: `fooDoesNotExist`.\n\nAvailable commands are: \`#{cmds.join("`, `")}\`.\n")
expect(err.docsUrl).to.eq("https://on.cypress.io/api")
done()
cy.get("body").command("fooDoesNotExist", "bar", "baz")

View File

@@ -0,0 +1,101 @@
const { _, $ } = Cypress
describe('src/cy/commands/commands', () => {
before(() => {
cy
.visit('/fixtures/dom.html')
.then(function (win) {
this.body = win.document.body.outerHTML
})
})
beforeEach(function () {
const doc = cy.state('document')
$(doc.body).empty().html(this.body)
})
it('can invoke commands by name', () => {
const body = cy.$$('body')
cy
.get('body').then(($body) => {
expect($body.get(0)).to.eq(body.get(0))
})
.command('get', 'body').then(($body) => {
expect($body.get(0)).to.eq(body.get(0))
})
})
it('can invoke child commands by name', () => {
const div = cy.$$('body>div:first')
cy
.get('body').find('div:first').then(($div) => {
expect($div.get(0)).to.eq(div.get(0))
})
.get('body').command('find', 'div:first').then(($div) => {
expect($div.get(0)).to.eq(div.get(0))
})
})
it('does not add cmds to cy.commands queue', () => {
cy.command('get', 'body').then(() => {
const names = cy.queue.names()
expect(names).to.deep.eq(['get', 'then'])
})
})
context('custom commands', () => {
beforeEach(() => {
Cypress.Commands.add('dashboard.selectWindows', () => {
cy
.get('[contenteditable]')
.first()
})
Cypress.Commands.add('login', { prevSubject: true }, (subject, email) => {
cy
.wrap(subject.find('input:first'))
.type(email)
})
})
it('works with custom commands', () => {
const input = cy.$$('input:first')
cy
.get('input:first')
.parent()
.command('login', 'brian@foo.com').then(($input) => {
expect($input.get(0)).to.eq(input.get(0))
})
})
it('works with namespaced commands', () => {
const ce = cy.$$('[contenteditable]').first()
cy
.command('dashboard.selectWindows').then(($ce) => {
expect($ce.get(0)).to.eq(ce.get(0))
})
})
})
context('errors', () => {
it('throws when cannot find command by name', (done) => {
cy.on('fail', (err) => {
const cmds = _.keys(Cypress.Chainer.prototype)
expect(cmds.length).to.be.gt(1)
expect(err.message).to.eq(`Could not find a command for: \`fooDoesNotExist\`.\n\nAvailable commands are: \`${cmds.join('`, `')}\`.\n`)
expect(err.docsUrl).to.eq('https://on.cypress.io/api')
done()
})
cy.get('body').command('fooDoesNotExist', 'bar', 'baz')
})
})
})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,78 +0,0 @@
Promise = Cypress.Promise
describe "src/cy/commands/debugging", ->
context "#debug", ->
beforeEach ->
@utilsLog = cy.stub(Cypress.utils, "log")
it "does not change the subject", ->
cy.wrap({}).debug().then (subject) ->
expect(subject).to.deep.eq({})
it "logs current subject", ->
obj = {foo: "bar"}
cy.wrap(obj).its("foo").debug().then ->
expect(@utilsLog).to.be.calledWithMatch("Current Subject: ", "bar")
it "logs previous command", ->
cy.wrap({}).debug().then ->
expect(@utilsLog).to.be.calledWithMatch("Command Name: ", "wrap")
expect(@utilsLog).to.be.calledWithMatch("Command Args: ", [{}])
expect(@utilsLog).to.be.calledWithMatch("Current Subject: ", {})
it "logs undefined on being parent", ->
cy.debug().then ->
expect(@utilsLog).to.be.calledWithMatch("Current Subject: ", undefined)
expect(@utilsLog).to.be.calledWithMatch("Command Name: ", undefined)
describe ".log", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
if attrs.name is "debug"
@lastLog = log
return null
it "can turn off logging", ->
cy
.wrap([], {log: false})
.debug({log: false}).then ->
expect(@lastLog).to.be.undefined
context "#pause", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
if attrs.name is "pause"
@lastLog = log
return null
it "can pause between each command and skips assertions", ->
expected = false
cy.once "paused", (name) =>
## should be pending
expect(@lastLog.get("state")).to.eq("pending")
expect(name).to.eq("wrap")
cy.once "paused", (name) ->
expected = true
expect(name).to.eq("then")
## resume the rest of the commands so this
## test ends
Cypress.emit("resume:all")
Cypress.emit("resume:next")
cy.pause().wrap({}).should("deep.eq", {}).then ->
expect(expected).to.be.true
## should be pending
expect(@lastLog.get("state")).to.eq("passed")
## should no longer have onPaused
expect(cy.state("onPaused")).to.be.null

View File

@@ -0,0 +1,102 @@
describe('src/cy/commands/debugging', () => {
context('#debug', () => {
beforeEach(function () {
this.utilsLog = cy.stub(Cypress.utils, 'log')
})
it('does not change the subject', () => {
cy.wrap({}).debug().then((subject) => {
expect(subject).to.deep.eq({})
})
})
it('logs current subject', () => {
const obj = { foo: 'bar' }
cy.wrap(obj).its('foo').debug().then(function () {
expect(this.utilsLog).to.be.calledWithMatch('Current Subject: ', 'bar')
})
})
it('logs previous command', () => {
cy.wrap({}).debug().then(function () {
expect(this.utilsLog).to.be.calledWithMatch('Command Name: ', 'wrap')
expect(this.utilsLog).to.be.calledWithMatch('Command Args: ', [{}])
expect(this.utilsLog).to.be.calledWithMatch('Current Subject: ', {})
})
})
it('logs undefined on being parent', () => {
cy.debug().then(function () {
expect(this.utilsLog).to.be.calledWithMatch('Current Subject: ', undefined)
expect(this.utilsLog).to.be.calledWithMatch('Command Name: ', undefined)
})
})
describe('.log', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'debug') {
this.lastLog = log
}
})
return null
})
it('can turn off logging', () => {
cy
.wrap([], { log: false })
.debug({ log: false }).then(function () {
expect(this.lastLog).to.be.undefined
})
})
})
})
context('#pause', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'pause') {
this.lastLog = log
}
})
return null
})
it('can pause between each command and skips assertions', function () {
let expected = false
cy.once('paused', (name) => {
// should be pending
expect(this.lastLog.get('state')).to.eq('pending')
expect(name).to.eq('wrap')
cy.once('paused', (name) => {
expected = true
expect(name).to.eq('then')
// resume the rest of the commands so this
// test ends
Cypress.emit('resume:all')
})
Cypress.emit('resume:next')
})
cy.pause().wrap({}).should('deep.eq', {}).then(function () {
expect(expected).to.be.true
// should be pending
expect(this.lastLog.get('state')).to.eq('passed')
// should no longer have onPaused
expect(cy.state('onPaused')).to.be.null
})
})
})
})

View File

@@ -1,306 +0,0 @@
_ = Cypress._
Promise = Cypress.Promise
describe "src/cy/commands/exec", ->
okResponse = { code: 0 }
context "#exec", ->
beforeEach ->
Cypress.config("execTimeout", 2500)
## call through normally on everything
cy.stub(Cypress, "backend").callThrough()
it "triggers 'exec' with the right options", ->
Cypress.backend.resolves(okResponse)
cy.exec("ls").then ->
expect(Cypress.backend).to.be.calledWith("exec", {
cmd: "ls"
timeout: 2500
env: {}
})
it "passes through environment variables", ->
Cypress.backend.resolves(okResponse)
cy.exec("ls", { env: { FOO: "foo" } }).then ->
expect(Cypress.backend).to.be.calledWith("exec", {
cmd: "ls"
timeout: 2500
env: {
FOO: "foo"
}
})
it "really works", ->
# output is trimmed
cy.exec("echo foo", { timeout: 20000 }).its("stdout").should("eq", "foo")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "can turn off logging", ->
Cypress.backend.resolves(okResponse)
cy.exec('ls', { log: false }).then ->
logs = _.filter @logs, (log) ->
log.get("name") is "exec"
expect(logs.length).to.eq(0)
it "logs immediately before resolving", ->
Cypress.backend.resolves(okResponse)
cy.on "log:added", (attrs, log) =>
if attrs.name is "exec"
expect(log.get("state")).to.eq("pending")
expect(log.get("message")).to.eq("ls")
cy.exec("ls").then =>
throw new Error("failed to log before resolving") unless @lastLog
describe "timeout", ->
it "defaults timeout to Cypress.config(execTimeout)", ->
Cypress.backend.resolves(okResponse)
timeout = cy.spy(Promise.prototype, "timeout")
cy.exec("ls").then ->
expect(timeout).to.be.calledWith(2500)
it "can override timeout", ->
Cypress.backend.resolves(okResponse)
timeout = cy.spy(Promise.prototype, "timeout")
cy.exec("li", { timeout: 1000 }).then ->
expect(timeout).to.be.calledWith(1000)
it "clears the current timeout and restores after success", ->
Cypress.backend.resolves(okResponse)
cy.timeout(100)
clearTimeout = cy.spy(cy, "clearTimeout")
cy.on "exec", =>
expect(clearTimeout).to.be.calledOnce
cy.exec("ls").then ->
expect(cy.timeout()).to.eq(100)
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 50)
@logs = []
cy.on "log:added", (attrs, log) =>
if attrs.name is "exec"
@lastLog = log
@logs.push(log)
return null
it "throws when cmd is absent", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: ''.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
`cy.exec()`
it "throws when cmd isn't a string", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: '3'.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec(3)
it "throws when cmd is an empty string", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: ''.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec('')
it "throws when the execution errors", (done) ->
Cypress.backend.rejects(new Error("exec failed"))
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.exec('ls')` failed with the following error:\n\n> \"Error: exec failed\"")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec("ls")
it "throws after timing out", (done) ->
Cypress.backend.resolves(Promise.delay(250))
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.exec('ls')` timed out after waiting `50ms`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec("ls", { timeout: 50 })
it "logs once on error", (done) ->
Cypress.backend.rejects(new Error("exec failed"))
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
done()
cy.exec("ls")
it "can timeout from the backend's response", (done) ->
err = new Error("timeout")
err.timedOut = true
Cypress.backend.rejects(err)
cy.on "fail", (err) ->
expect(err.message).to.include("`cy.exec('sleep 2')` timed out after waiting `100ms`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec("sleep 2", {
timeout: 100
})
it "can really time out", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.include("`cy.exec('sleep 2')` timed out after waiting `100ms`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/exec")
done()
cy.exec("sleep 2", {
timeout: 100
})
describe "when error code is non-zero", ->
it "throws error that includes useful information and exit code", (done) ->
Cypress.backend.resolves({ code: 1 })
cy.on "fail", (err) ->
expect(err.message).to.contain("`cy.exec('ls')` failed because the command exited with a non-zero code.\n\nPass `{failOnNonZeroExit: false}` to ignore exit code failures.")
expect(err.message).to.contain("Code: 1")
expect(err.docsUrl).to.contain("https://on.cypress.io/exec")
done()
cy.exec("ls")
it "throws error that includes stderr if it exists and is non-empty", (done) ->
Cypress.backend.resolves({ code: 1, stderr: "error output", stdout: "" })
cy.on "fail", (err) ->
expect(err.message).to.contain("Stderr:\nerror output")
expect(err.message).not.to.contain("Stdout")
done()
cy.exec("ls")
it "throws error that includes stdout if it exists and is non-empty", (done) ->
Cypress.backend.resolves({ code: 1, stderr: "", stdout: "regular output" })
cy.on "fail", (err) ->
expect(err.message).to.contain("\nStdout:\nregular output")
expect(err.message).not.to.contain("Stderr")
done()
cy.exec("ls")
it "throws error that includes stdout and stderr if they exists and are non-empty", (done) ->
Cypress.backend.resolves({ code: 1, stderr: "error output", stdout: "regular output" })
cy.on "fail", (err) ->
expect(err.message).to.contain("\nStdout:\nregular output\nStderr:\nerror output")
done()
cy.exec("ls")
it "truncates the stdout and stderr in the error message", (done) ->
Cypress.backend.resolves({
code: 1
stderr: "#{_.range(200).join()}stderr should be truncated"
stdout: "#{_.range(200).join()}stdout should be truncated"
})
cy.on "fail", (err) ->
expect(err.message).not.to.contain("stderr should be truncated")
expect(err.message).not.to.contain("stdout should be truncated")
expect(err.message).to.contain("...")
done()
cy.exec("ls")
it "can really fail", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
{ Yielded } = lastLog.invoke("consoleProps")
# output is trimmed
expect(Yielded).to.deep.eq({
stdout: "foo"
stderr: ""
code: 1
})
done()
cy.exec("echo foo && exit 1")
describe "and failOnNonZeroExit is false", ->
it "does not error", ->
response = { code: 1, stderr: "error output", stdout: "regular output" }
Cypress.backend.resolves(response)
cy
.exec("ls", { failOnNonZeroExit: false })
.should("deep.eq", response)
it "does not really fail", ->
cy.exec("echo foo && exit 1", {
failOnNonZeroExit: false
})

View File

@@ -0,0 +1,384 @@
const { _, Promise } = Cypress
describe('src/cy/commands/exec', () => {
const okResponse = { code: 0 }
context('#exec', () => {
beforeEach(() => {
Cypress.config('execTimeout', 2500)
// call through normally on everything
cy.stub(Cypress, 'backend').callThrough()
})
it('triggers \'exec\' with the right options', () => {
Cypress.backend.resolves(okResponse)
cy.exec('ls').then(() => {
expect(Cypress.backend).to.be.calledWith('exec', {
cmd: 'ls',
timeout: 2500,
env: {},
})
})
})
it('passes through environment variables', () => {
Cypress.backend.resolves(okResponse)
cy.exec('ls', { env: { FOO: 'foo' } }).then(() => {
expect(Cypress.backend).to.be.calledWith('exec', {
cmd: 'ls',
timeout: 2500,
env: {
FOO: 'foo',
},
})
})
})
it('really works', () => {
// output is trimmed
cy.exec('echo foo', { timeout: 20000 }).its('stdout').should('eq', 'foo')
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('can turn off logging', () => {
Cypress.backend.resolves(okResponse)
cy.exec('ls', { log: false }).then(function () {
const logs = _.filter(this.logs, (log) => {
return log.get('name') === 'exec'
})
expect(logs.length).to.eq(0)
})
})
it('logs immediately before resolving', function () {
Cypress.backend.resolves(okResponse)
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'exec') {
expect(log.get('state')).to.eq('pending')
expect(log.get('message')).to.eq('ls')
}
})
cy.exec('ls').then(() => {
if (!this.lastLog) {
throw new Error('failed to log before resolving')
}
})
})
})
describe('timeout', () => {
it('defaults timeout to Cypress.config(execTimeout)', () => {
Cypress.backend.resolves(okResponse)
const timeout = cy.spy(Promise.prototype, 'timeout')
cy.exec('ls').then(() => {
expect(timeout).to.be.calledWith(2500)
})
})
it('can override timeout', () => {
Cypress.backend.resolves(okResponse)
const timeout = cy.spy(Promise.prototype, 'timeout')
cy.exec('li', { timeout: 1000 }).then(() => {
expect(timeout).to.be.calledWith(1000)
})
})
it('clears the current timeout and restores after success', () => {
Cypress.backend.resolves(okResponse)
cy.timeout(100)
const clearTimeout = cy.spy(cy, 'clearTimeout')
cy.on('exec', () => {
expect(clearTimeout).to.be.calledOnce
})
cy.exec('ls').then(() => {
expect(cy.timeout()).to.eq(100)
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 50)
this.logs = []
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'exec') {
this.lastLog = log
this.logs.push(log)
}
})
return null
})
it('throws when cmd is absent', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: \'\'.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec()
})
it('throws when cmd isn\'t a string', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: \'3\'.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec(3)
})
it('throws when cmd is an empty string', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.exec()` must be passed a non-empty string as its 1st argument. You passed: \'\'.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec('')
})
it('throws when the execution errors', function (done) {
Cypress.backend.rejects(new Error('exec failed'))
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.exec(\'ls\')` failed with the following error:\n\n> "Error: exec failed"')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec('ls')
})
it('throws after timing out', function (done) {
Cypress.backend.resolves(Promise.delay(250))
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.exec(\'ls\')` timed out after waiting `50ms`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec('ls', { timeout: 50 })
})
it('logs once on error', function (done) {
Cypress.backend.rejects(new Error('exec failed'))
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
done()
})
cy.exec('ls')
})
it('can timeout from the backend\'s response', (done) => {
const err = new Error('timeout')
err.timedOut = true
Cypress.backend.rejects(err)
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.exec(\'sleep 2\')` timed out after waiting `100ms`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec('sleep 2', {
timeout: 100,
})
})
it('can really time out', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.exec(\'sleep 2\')` timed out after waiting `100ms`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/exec')
done()
})
cy.exec('sleep 2', {
timeout: 100,
})
})
describe('when error code is non-zero', () => {
it('throws error that includes useful information and exit code', (done) => {
Cypress.backend.resolves({ code: 1 })
cy.on('fail', (err) => {
expect(err.message).to.contain('`cy.exec(\'ls\')` failed because the command exited with a non-zero code.\n\nPass `{failOnNonZeroExit: false}` to ignore exit code failures.')
expect(err.message).to.contain('Code: 1')
expect(err.docsUrl).to.contain('https://on.cypress.io/exec')
done()
})
cy.exec('ls')
})
it('throws error that includes stderr if it exists and is non-empty', (done) => {
Cypress.backend.resolves({ code: 1, stderr: 'error output', stdout: '' })
cy.on('fail', (err) => {
expect(err.message).to.contain('Stderr:\nerror output')
expect(err.message).not.to.contain('Stdout')
done()
})
cy.exec('ls')
})
it('throws error that includes stdout if it exists and is non-empty', (done) => {
Cypress.backend.resolves({ code: 1, stderr: '', stdout: 'regular output' })
cy.on('fail', (err) => {
expect(err.message).to.contain('\nStdout:\nregular output')
expect(err.message).not.to.contain('Stderr')
done()
})
cy.exec('ls')
})
it('throws error that includes stdout and stderr if they exists and are non-empty', (done) => {
Cypress.backend.resolves({ code: 1, stderr: 'error output', stdout: 'regular output' })
cy.on('fail', (err) => {
expect(err.message).to.contain('\nStdout:\nregular output\nStderr:\nerror output')
done()
})
cy.exec('ls')
})
it('truncates the stdout and stderr in the error message', (done) => {
Cypress.backend.resolves({
code: 1,
stderr: `${_.range(200).join()}stderr should be truncated`,
stdout: `${_.range(200).join()}stdout should be truncated`,
})
cy.on('fail', (err) => {
expect(err.message).not.to.contain('stderr should be truncated')
expect(err.message).not.to.contain('stdout should be truncated')
expect(err.message).to.contain('...')
done()
})
cy.exec('ls')
})
it('can really fail', function (done) {
cy.on('fail', () => {
const { lastLog } = this
const { Yielded } = lastLog.invoke('consoleProps')
// output is trimmed
expect(Yielded).to.deep.eq({
stdout: 'foo',
stderr: '',
code: 1,
})
done()
})
cy.exec('echo foo && exit 1')
})
describe('and failOnNonZeroExit is false', () => {
it('does not error', () => {
const response = { code: 1, stderr: 'error output', stdout: 'regular output' }
Cypress.backend.resolves(response)
cy
.exec('ls', { failOnNonZeroExit: false })
.should('deep.eq', response)
})
it('does not really fail', () => {
cy.exec('echo foo && exit 1', {
failOnNonZeroExit: false,
})
})
})
})
})
})
})

View File

@@ -1,464 +0,0 @@
_ = Cypress._
okResponse = {
contents: "contents"
filePath: "/path/to/foo.json"
}
describe "src/cy/commands/files", ->
beforeEach ->
## call through normally on everything
cy.stub(Cypress, "backend").callThrough()
describe "#readFile", ->
it "triggers 'read:file' with the right options", ->
Cypress.backend.resolves(okResponse)
cy.readFile("foo.json").then ->
expect(Cypress.backend).to.be.calledWith(
"read:file",
"foo.json",
{ encoding: "utf8" }
)
it "can take encoding as second argument", ->
Cypress.backend.resolves(okResponse)
cy.readFile("foo.json", "ascii").then ->
expect(Cypress.backend).to.be.calledWith(
"read:file",
"foo.json",
{ encoding: "ascii" }
)
it "sets the contents as the subject", ->
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json').then (subject) ->
expect(subject).to.equal("contents")
it "retries to read when ENOENT", ->
err = new Error("foo")
err.code = "ENOENT"
retries = 0
cy.on "command:retry", ->
retries += 1
Cypress.backend
.onFirstCall()
.rejects(err)
.onSecondCall()
.resolves(okResponse)
cy.readFile('foo.json').then ->
expect(retries).to.eq(1)
it "retries assertions until they pass", ->
retries = 0
cy.on "command:retry", ->
retries += 1
Cypress.backend
.onFirstCall()
.resolves({
contents: "foobarbaz"
})
.onSecondCall()
.resolves({
contents: "quux"
})
cy.readFile("foo.json").should("eq", "quux").then ->
expect(retries).to.eq(1)
it "really works", ->
cy.readFile("cypress.json").its("baseUrl").should("eq", "http://localhost:3500")
it "works when contents are supposed to be null", ->
cy.readFile("does-not-exist").should("be.null")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "can turn off logging", ->
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json', { log: false }).then ->
logs = _.filter @logs, (log) ->
log.get("name") is "readFile"
expect(logs.length).to.eq(0)
it "logs immediately before resolving", ->
Cypress.backend.resolves(okResponse)
cy.on "log:added", (attrs, log) =>
if attrs.name is "readFile"
expect(log.get("state")).to.eq("pending")
expect(log.get("message")).to.eq("foo.json")
cy.readFile("foo.json").then =>
throw new Error("failed to log before resolving") unless @lastLog
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 50)
@logs = []
cy.on "log:added", (attrs, log) =>
if attrs.name is "readFile"
@lastLog = log
@logs.push(log)
return null
it "throws when file argument is absent", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: `undefined`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
`cy.readFile()`
it "throws when file argument is not a string", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: `2`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
cy.readFile(2)
it "throws when file argument is an empty string", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: ``.")
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
cy.readFile("")
it "throws when there is an error reading the file", (done) ->
err = new Error("EISDIR: illegal operation on a directory, read")
err.name = "EISDIR"
err.code = "EISDIR"
err.filePath = "/path/to/foo"
Cypress.backend.rejects(err)
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq """
`cy.readFile(\"foo\")` failed while trying to read the file at the following path:
`/path/to/foo`
The following error occurred:
> "EISDIR: illegal operation on a directory, read"
"""
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
cy.readFile("foo")
it "has implicit existence assertion and throws a specific error when file does not exist", (done) ->
err = new Error("ENOENT: no such file or directory, open 'foo.json'")
err.name = "ENOENT"
err.code = "ENOENT"
err.filePath = "/path/to/foo.json"
Cypress.backend.rejects(err)
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("""Timed out retrying: `cy.readFile(\"foo.json\")` failed because the file does not exist at the following path:
`/path/to/foo.json`
""")
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
cy.readFile("foo.json")
it "throws a specific error when file exists when it shouldn't", (done) ->
Cypress.backend.resolves(okResponse)
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("""
Timed out retrying: `cy.readFile(\"foo.json\")` failed because the file exists when expected not to exist at the following path:
`/path/to/foo.json`
""")
expect(err.docsUrl).to.eq("https://on.cypress.io/readfile")
done()
cy.readFile("foo.json").should("not.exist")
it "passes through assertion error when not about existence", (done) ->
Cypress.backend.resolves({
contents: "foo"
})
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("Timed out retrying: expected 'foo' to equal 'contents'")
done()
cy.readFile("foo.json").should("equal", "contents")
describe "#writeFile", ->
it "triggers 'write:file' with the right options", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents").then ->
expect(Cypress.backend).to.be.calledWith(
"write:file",
"foo.txt",
"contents",
{
encoding: "utf8"
flag: "w"
}
)
it "can take encoding as third argument", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents", "ascii").then ->
expect(Cypress.backend).to.be.calledWith(
"write:file",
"foo.txt",
"contents",
{
encoding: "ascii"
flag: "w"
}
)
it "can take encoding as part of options", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents", {encoding: "ascii"}).then ->
expect(Cypress.backend).to.be.calledWith(
"write:file",
"foo.txt",
"contents",
{
encoding: "ascii"
flag: "w"
}
)
it "yields null", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents").then (subject) ->
expect(subject).to.not.exist
it "can write a string", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents")
it "can write an array as json", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.json", [])
it "can write an object as json", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.json", {})
it "writes the file to the filesystem, overwriting existing file", ->
cy
.writeFile("cypress/fixtures/foo.txt", "")
.writeFile("cypress/fixtures/foo.txt", "bar")
.readFile("cypress/fixtures/foo.txt").should("equal", "bar")
.exec("rm cypress/fixtures/foo.txt")
describe ".flag", ->
it "sends a flag if specified", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents", { flag: "a+" }).then ->
expect(Cypress.backend).to.be.calledWith(
"write:file",
"foo.txt",
"contents",
{
encoding: "utf8",
flag: "a+"
})
it "appends content to existing file if specified", ->
cy
.writeFile("cypress/fixtures/foo.txt", "foo")
.writeFile("cypress/fixtures/foo.txt", "bar", { flag: "a+"})
.readFile("cypress/fixtures/foo.txt").should("equal", "foobar")
.exec("rm cypress/fixtures/foo.txt")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "can turn off logging", ->
Cypress.backend.resolves(okResponse)
cy.writeFile("foo.txt", "contents", { log: false }).then ->
logs = _.filter @logs, (log) ->
log.get("name") is "writeFile"
expect(logs.length).to.eq(0)
it "logs immediately before resolving", ->
Cypress.backend.resolves(okResponse)
cy.on "log:added", (attrs, log) =>
if attrs.name is "writeFile"
expect(log.get("state")).to.eq("pending")
expect(log.get("message")).to.eq("foo.txt", "contents")
cy.writeFile("foo.txt", "contents").then =>
throw new Error("failed to log before resolving") unless @lastLog
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 50)
@logs = []
cy.on "log:added", (attrs, log) =>
if attrs.name is "writeFile"
@lastLog = log
@logs.push(log)
return null
it "throws when file name argument is absent", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.writeFile()` must be passed a non-empty string as its 1st argument. You passed: `undefined`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/writefile")
done()
`cy.writeFile()`
it "throws when file name argument is not a string", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.writeFile()` must be passed a non-empty string as its 1st argument. You passed: `2`.")
expect(err.docsUrl).to.eq("https://on.cypress.io/writefile")
done()
cy.writeFile(2)
it "throws when contents argument is absent", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.writeFile()` must be passed a non-empty string, an object, or an array as its 2nd argument. You passed: `undefined`.")
done()
cy.writeFile("foo.txt")
it "throws when contents argument is not a string, object, or array", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq("`cy.writeFile()` must be passed a non-empty string, an object, or an array as its 2nd argument. You passed: `2`.")
done()
cy.writeFile("foo.txt", 2)
it "throws when there is an error writing the file", (done) ->
err = new Error("WHOKNOWS: unable to write file")
err.name = "WHOKNOWS"
err.code = "WHOKNOWS"
err.filePath = "/path/to/foo.txt"
Cypress.backend.rejects(err)
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(err.message).to.eq """`cy.writeFile(\"foo.txt\")` failed while trying to write the file at the following path:
`/path/to/foo.txt`
The following error occurred:
> "WHOKNOWS: unable to write file"
"""
expect(err.docsUrl).to.eq("https://on.cypress.io/writefile")
done()
cy.writeFile("foo.txt", "contents")

View File

@@ -0,0 +1,572 @@
const { stripIndent } = require('common-tags')
const { _ } = Cypress
const okResponse = {
contents: 'contents',
filePath: '/path/to/foo.json',
}
describe('src/cy/commands/files', () => {
beforeEach(() => {
// call through normally on everything
cy.stub(Cypress, 'backend').callThrough()
})
describe('#readFile', () => {
it('triggers \'read:file\' with the right options', () => {
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json').then(() => {
expect(Cypress.backend).to.be.calledWith(
'read:file',
'foo.json',
{ encoding: 'utf8' },
)
})
})
it('can take encoding as second argument', () => {
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json', 'ascii').then(() => {
expect(Cypress.backend).to.be.calledWith(
'read:file',
'foo.json',
{ encoding: 'ascii' },
)
})
})
it('sets the contents as the subject', () => {
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json').then((subject) => {
expect(subject).to.equal('contents')
})
})
it('retries to read when ENOENT', () => {
const err = new Error('foo')
err.code = 'ENOENT'
let retries = 0
cy.on('command:retry', () => {
retries += 1
})
Cypress.backend
.onFirstCall()
.rejects(err)
.onSecondCall()
.resolves(okResponse)
cy.readFile('foo.json').then(() => {
expect(retries).to.eq(1)
})
})
it('retries assertions until they pass', () => {
let retries = 0
cy.on('command:retry', () => {
retries += 1
})
Cypress.backend
.onFirstCall()
.resolves({
contents: 'foobarbaz',
})
.onSecondCall()
.resolves({
contents: 'quux',
})
cy.readFile('foo.json').should('eq', 'quux').then(() => {
expect(retries).to.eq(1)
})
})
it('really works', () => {
cy.readFile('cypress.json').its('baseUrl').should('eq', 'http://localhost:3500')
})
it('works when contents are supposed to be null', () => {
cy.readFile('does-not-exist').should('be.null')
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('can turn off logging', () => {
Cypress.backend.resolves(okResponse)
cy.readFile('foo.json', { log: false }).then(function () {
const logs = _.filter(this.logs, (log) => {
return log.get('name') === 'readFile'
})
expect(logs.length).to.eq(0)
})
})
it('logs immediately before resolving', function () {
Cypress.backend.resolves(okResponse)
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'readFile') {
expect(log.get('state')).to.eq('pending')
expect(log.get('message')).to.eq('foo.json')
}
})
cy.readFile('foo.json').then(() => {
if (!this.lastLog) {
throw new Error('failed to log before resolving')
}
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 50)
this.logs = []
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'readFile') {
this.lastLog = log
this.logs.push(log)
}
})
return null
})
it('throws when file argument is absent', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: `undefined`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile()
})
it('throws when file argument is not a string', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: `2`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile(2)
})
it('throws when file argument is an empty string', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.readFile()` must be passed a non-empty string as its 1st argument. You passed: ``.')
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile('')
})
it('throws when there is an error reading the file', function (done) {
const err = new Error('EISDIR: illegal operation on a directory, read')
err.name = 'EISDIR'
err.code = 'EISDIR'
err.filePath = '/path/to/foo'
Cypress.backend.rejects(err)
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq(stripIndent`\
\`cy.readFile(\"foo\")\` failed while trying to read the file at the following path:
\`/path/to/foo\`
The following error occurred:
> "EISDIR: illegal operation on a directory, read"`)
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile('foo')
})
it('has implicit existence assertion and throws a specific error when file does not exist', function (done) {
const err = new Error('ENOENT: no such file or directory, open \'foo.json\'')
err.name = 'ENOENT'
err.code = 'ENOENT'
err.filePath = '/path/to/foo.json'
Cypress.backend.rejects(err)
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq(stripIndent`
Timed out retrying: \`cy.readFile(\"foo.json\")\` failed because the file does not exist at the following path:
\`/path/to/foo.json\``)
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile('foo.json')
})
it('throws a specific error when file exists when it shouldn\'t', function (done) {
Cypress.backend.resolves(okResponse)
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq(stripIndent`\
Timed out retrying: \`cy.readFile(\"foo.json\")\` failed because the file exists when expected not to exist at the following path:
\`/path/to/foo.json\``)
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
done()
})
cy.readFile('foo.json').should('not.exist')
})
it('passes through assertion error when not about existence', function (done) {
Cypress.backend.resolves({
contents: 'foo',
})
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('Timed out retrying: expected \'foo\' to equal \'contents\'')
done()
})
cy.readFile('foo.json').should('equal', 'contents')
})
})
})
describe('#writeFile', () => {
it('triggers \'write:file\' with the right options', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents').then(() => {
expect(Cypress.backend).to.be.calledWith(
'write:file',
'foo.txt',
'contents',
{
encoding: 'utf8',
flag: 'w',
},
)
})
})
it('can take encoding as third argument', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents', 'ascii').then(() => {
expect(Cypress.backend).to.be.calledWith(
'write:file',
'foo.txt',
'contents',
{
encoding: 'ascii',
flag: 'w',
},
)
})
})
it('can take encoding as part of options', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents', { encoding: 'ascii' }).then(() => {
expect(Cypress.backend).to.be.calledWith(
'write:file',
'foo.txt',
'contents',
{
encoding: 'ascii',
flag: 'w',
},
)
})
})
it('yields null', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents').then((subject) => {
expect(subject).to.not.exist
})
})
it('can write a string', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents')
})
it('can write an array as json', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.json', [])
})
it('can write an object as json', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.json', {})
})
it('writes the file to the filesystem, overwriting existing file', () => {
cy
.writeFile('cypress/fixtures/foo.txt', '')
.writeFile('cypress/fixtures/foo.txt', 'bar')
.readFile('cypress/fixtures/foo.txt').should('equal', 'bar')
.exec('rm cypress/fixtures/foo.txt')
})
describe('.flag', () => {
it('sends a flag if specified', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents', { flag: 'a+' }).then(() => {
expect(Cypress.backend).to.be.calledWith(
'write:file',
'foo.txt',
'contents',
{
encoding: 'utf8',
flag: 'a+',
},
)
})
})
it('appends content to existing file if specified', () => {
cy
.writeFile('cypress/fixtures/foo.txt', 'foo')
.writeFile('cypress/fixtures/foo.txt', 'bar', { flag: 'a+' })
.readFile('cypress/fixtures/foo.txt').should('equal', 'foobar')
.exec('rm cypress/fixtures/foo.txt')
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('can turn off logging', () => {
Cypress.backend.resolves(okResponse)
cy.writeFile('foo.txt', 'contents', { log: false }).then(function () {
const logs = _.filter(this.logs, (log) => {
return log.get('name') === 'writeFile'
})
expect(logs.length).to.eq(0)
})
})
it('logs immediately before resolving', function () {
Cypress.backend.resolves(okResponse)
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'writeFile') {
expect(log.get('state')).to.eq('pending')
expect(log.get('message')).to.eq('foo.txt', 'contents')
}
})
cy.writeFile('foo.txt', 'contents').then(() => {
if (!this.lastLog) {
throw new Error('failed to log before resolving')
}
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 50)
this.logs = []
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'writeFile') {
this.lastLog = log
this.logs.push(log)
}
})
return null
})
it('throws when file name argument is absent', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.writeFile()` must be passed a non-empty string as its 1st argument. You passed: `undefined`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
done()
})
cy.writeFile()
})
it('throws when file name argument is not a string', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.writeFile()` must be passed a non-empty string as its 1st argument. You passed: `2`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
done()
})
cy.writeFile(2)
})
it('throws when contents argument is absent', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.writeFile()` must be passed a non-empty string, an object, or an array as its 2nd argument. You passed: `undefined`.')
done()
})
cy.writeFile('foo.txt')
})
it('throws when contents argument is not a string, object, or array', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq('`cy.writeFile()` must be passed a non-empty string, an object, or an array as its 2nd argument. You passed: `2`.')
done()
})
cy.writeFile('foo.txt', 2)
})
it('throws when there is an error writing the file', function (done) {
const err = new Error('WHOKNOWS: unable to write file')
err.name = 'WHOKNOWS'
err.code = 'WHOKNOWS'
err.filePath = '/path/to/foo.txt'
Cypress.backend.rejects(err)
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(err.message).to.eq(stripIndent`
\`cy.writeFile(\"foo.txt\")\` failed while trying to write the file at the following path:
\`/path/to/foo.txt\`
The following error occurred:
> "WHOKNOWS: unable to write file"`)
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
done()
})
cy.writeFile('foo.txt', 'contents')
})
})
})
})

View File

@@ -1,208 +0,0 @@
_ = Cypress._
Promise = Cypress.Promise
describe "src/cy/commands/fixtures", ->
beforeEach ->
Cypress.emit("clear:fixtures:cache")
## call all of the fixture triggers async to simulate
## the real browser environment
context "#fixture", ->
beforeEach ->
## call through normally on everything
cy.stub(Cypress, "backend").callThrough()
it "triggers 'fixture' on Cypress", ->
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
cy.fixture("foo").as("f").then (obj) ->
expect(obj).to.deep.eq {foo: "bar"}
expect(Cypress.backend).to.be.calledWith("get:fixture", "foo", {})
it "can support an array of fixtures"
it "can have encoding as second argument", ->
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
cy.fixture("foo", "ascii").then (obj) ->
expect(obj).to.deep.eq {foo: "bar"}
expect(Cypress.backend).to.be.calledWith("get:fixture", "foo", {
encoding: "ascii"
})
it "can have encoding as second argument and options as third argument", ->
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
cy.fixture("foo", "ascii", {timeout: 1000}).then (obj) ->
expect(obj).to.deep.eq {foo: "bar"}
expect(Cypress.backend).to.be.calledWith("get:fixture", "foo", {
encoding: "ascii"
})
it "really works", ->
cy.fixture("example").should("deep.eq", { example: true })
it "can read a fixture without extension with multiple dots in the name", ->
cy.fixture("foo.bar.baz").should("deep.eq", { quux: "quuz" })
it "looks for csv without extension", ->
cy.fixture("comma-separated").should "equal", """
One,Two,Three
1,2,3
"""
it "handles files with unknown extensions, reading them as utf-8", ->
cy.fixture("yaml.yaml").should "equal", """
- foo
- bar
- 
"""
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 50)
@logs = []
cy.on "log:added", (attrs, log) =>
if attrs.name is "fixture"
@lastLog = log
@logs.push(log)
return null
it "throws if fixturesFolder is set to false", (done) ->
Cypress.config("fixturesFolder", false)
cy.on "fail", =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error").message).to.eq("`cy.fixture()` is not valid because you have configured `fixturesFolder` to `false`.")
expect(lastLog.get("error").docsUrl).to.eq("https://on.cypress.io/fixture")
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("name")).to.eq "fixture"
done()
cy.fixture("foo")
it "throws when fixture cannot be found without extension", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("name")).to.eq "fixture"
expect(lastLog.get("message")).to.eq "err"
expect(err.message).to.include "A fixture file could not be found"
expect(err.message).to.include "cypress/fixtures/err"
done()
cy.fixture("err")
it "throws when fixture cannot be found with extension", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("name")).to.eq "fixture"
expect(lastLog.get("message")).to.eq "err.txt"
expect(err.message).to.include "A fixture file could not be found"
expect(err.message).to.include "cypress/fixtures/err.txt"
done()
cy.fixture("err.txt")
it "throws after timing out", (done) ->
Cypress.backend.withArgs("get:fixture").resolves(Promise.delay(1000))
cy.on "fail", (err) =>
lastLog = @lastLog
expect(@logs.length).to.eq(1)
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("name")).to.eq "fixture"
expect(lastLog.get("message")).to.eq "foo, {timeout: 50}"
expect(err.message).to.eq("`cy.fixture()` timed out waiting `50ms` to receive a fixture. No fixture was ever sent by the server.")
expect(err.docsUrl).to.eq("https://on.cypress.io/fixture")
done()
cy.fixture("foo", {timeout: 50})
describe "timeout", ->
it "sets timeout to Cypress.config(responseTimeout)", ->
Cypress.config("responseTimeout", 2500)
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
timeout = cy.spy(Promise.prototype, "timeout")
cy.fixture("foo").then ->
expect(timeout).to.be.calledWith(2500)
it "can override timeout", ->
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
timeout = cy.spy(Promise.prototype, "timeout")
cy.fixture("foobar", {timeout: 1000}).then ->
expect(timeout).to.be.calledWith(1000)
it "clears the current timeout and restores after success", ->
Cypress.backend.withArgs("get:fixture").resolves({foo: "bar"})
cy.timeout(100)
cy.spy(cy, "clearTimeout")
cy.fixture("foo").then ->
expect(cy.clearTimeout).to.be.calledWith("get:fixture")
## restores the timeout afterwards
expect(cy.timeout()).to.eq(100)
describe "caching", ->
beforeEach ->
Cypress.backend
.withArgs("get:fixture", "foo")
.resolves({foo: "bar"})
.withArgs("get:fixture", "bar")
.resolves({bar: "baz"})
it "caches fixtures by name", ->
cy.fixture("foo").then (obj) =>
expect(obj).to.deep.eq({foo: "bar"})
cy.fixture("bar").then (obj) =>
expect(obj).to.deep.eq {bar: "baz"}
cy.fixture("foo").then (obj) =>
expect(obj).to.deep.eq {foo: "bar"}
.then ->
expect(Cypress.backend).to.be.calledTwice
it "clones fixtures to prevent accidental mutation", ->
cy.fixture("foo").then (obj) ->
## mutate the object
obj.baz = "quux"
cy.fixture("foo").then (obj2) ->
obj2.lorem = "ipsum"
expect(obj2).not.to.have.property("baz")
cy.fixture("foo").then (obj3) ->
expect(obj3).not.to.have.property("lorem")
.then ->
expect(Cypress.backend).to.be.calledOnce

View File

@@ -0,0 +1,250 @@
const { Promise } = Cypress
describe('src/cy/commands/fixtures', () => {
beforeEach(() => {
return Cypress.emit('clear:fixtures:cache')
})
// call all of the fixture triggers async to simulate
// the real browser environment
context('#fixture', () => {
beforeEach(() => {
// call through normally on everything
cy.stub(Cypress, 'backend').callThrough()
})
it('triggers \'fixture\' on Cypress', () => {
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
cy.fixture('foo').as('f').then((obj) => {
expect(obj).to.deep.eq({ foo: 'bar' })
expect(Cypress.backend).to.be.calledWith('get:fixture', 'foo', {})
})
})
it('can support an array of fixtures')
it('can have encoding as second argument', () => {
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
cy.fixture('foo', 'ascii').then((obj) => {
expect(obj).to.deep.eq({ foo: 'bar' })
expect(Cypress.backend).to.be.calledWith('get:fixture', 'foo', {
encoding: 'ascii',
})
})
})
it('can have encoding as second argument and options as third argument', () => {
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
cy.fixture('foo', 'ascii', { timeout: 1000 }).then((obj) => {
expect(obj).to.deep.eq({ foo: 'bar' })
expect(Cypress.backend).to.be.calledWith('get:fixture', 'foo', {
encoding: 'ascii',
})
})
})
it('really works', () => {
cy.fixture('example').should('deep.eq', { example: true })
})
it('can read a fixture without extension with multiple dots in the name', () => {
cy.fixture('foo.bar.baz').should('deep.eq', { quux: 'quuz' })
})
it('looks for csv without extension', () => {
cy.fixture('comma-separated').should('equal', [
'One,Two,Three\n',
'1,2,3\n',
].join(''))
})
it('handles files with unknown extensions, reading them as utf-8', () => {
cy.fixture('yaml.yaml').should('equal', [
'- foo\n',
'- bar\n',
'- \n',
].join(''))
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 50)
this.logs = []
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'fixture') {
this.lastLog = log
this.logs.push(log)
}
})
return null
})
it('throws if fixturesFolder is set to false', function (done) {
Cypress.config('fixturesFolder', false)
cy.on('fail', () => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error').message).to.eq('`cy.fixture()` is not valid because you have configured `fixturesFolder` to `false`.')
expect(lastLog.get('error').docsUrl).to.eq('https://on.cypress.io/fixture')
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('name')).to.eq('fixture')
done()
})
cy.fixture('foo')
})
it('throws when fixture cannot be found without extension', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('name')).to.eq('fixture')
expect(lastLog.get('message')).to.eq('err')
expect(err.message).to.include('A fixture file could not be found')
expect(err.message).to.include('cypress/fixtures/err')
done()
})
cy.fixture('err')
})
it('throws when fixture cannot be found with extension', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('name')).to.eq('fixture')
expect(lastLog.get('message')).to.eq('err.txt')
expect(err.message).to.include('A fixture file could not be found')
expect(err.message).to.include('cypress/fixtures/err.txt')
done()
})
cy.fixture('err.txt')
})
it('throws after timing out', function (done) {
Cypress.backend.withArgs('get:fixture').resolves(Promise.delay(1000))
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('error')).to.eq(err)
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('name')).to.eq('fixture')
expect(lastLog.get('message')).to.eq('foo, {timeout: 50}')
expect(err.message).to.eq('`cy.fixture()` timed out waiting `50ms` to receive a fixture. No fixture was ever sent by the server.')
expect(err.docsUrl).to.eq('https://on.cypress.io/fixture')
done()
})
cy.fixture('foo', { timeout: 50 })
})
})
describe('timeout', () => {
it('sets timeout to Cypress.config(responseTimeout)', () => {
Cypress.config('responseTimeout', 2500)
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
const timeout = cy.spy(Promise.prototype, 'timeout')
cy.fixture('foo').then(() => {
expect(timeout).to.be.calledWith(2500)
})
})
it('can override timeout', () => {
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
const timeout = cy.spy(Promise.prototype, 'timeout')
cy.fixture('foobar', { timeout: 1000 }).then(() => {
expect(timeout).to.be.calledWith(1000)
})
})
it('clears the current timeout and restores after success', () => {
Cypress.backend.withArgs('get:fixture').resolves({ foo: 'bar' })
cy.timeout(100)
cy.spy(cy, 'clearTimeout')
cy.fixture('foo').then(() => {
expect(cy.clearTimeout).to.be.calledWith('get:fixture')
// restores the timeout afterwards
expect(cy.timeout()).to.eq(100)
})
})
})
describe('caching', () => {
beforeEach(() => {
Cypress.backend
.withArgs('get:fixture', 'foo')
.resolves({ foo: 'bar' })
.withArgs('get:fixture', 'bar')
.resolves({ bar: 'baz' })
})
it('caches fixtures by name', () => {
cy.fixture('foo').then((obj) => {
expect(obj).to.deep.eq({ foo: 'bar' })
cy.fixture('bar').then((obj) => {
expect(obj).to.deep.eq({ bar: 'baz' })
cy.fixture('foo').then((obj) => {
expect(obj).to.deep.eq({ foo: 'bar' })
})
})
})
.then(() => {
expect(Cypress.backend).to.be.calledTwice
})
})
it('clones fixtures to prevent accidental mutation', () => {
cy.fixture('foo').then((obj) => {
// mutate the object
obj.baz = 'quux'
cy.fixture('foo').then((obj2) => {
obj2.lorem = 'ipsum'
expect(obj2).not.to.have.property('baz')
cy.fixture('foo').then((obj3) => {
expect(obj3).not.to.have.property('lorem')
})
})
.then(() => {
expect(Cypress.backend).to.be.calledOnce
})
})
})
})
})
})

View File

@@ -1,86 +0,0 @@
describe "src/cy/commands/local_storage", ->
context "#clearLocalStorage", ->
it "passes keys onto Cypress.LocalStorage.clear", ->
clear = cy.spy Cypress.LocalStorage, "clear"
cy.clearLocalStorage("foo").then ->
expect(clear).to.be.calledWith "foo"
it "sets the storages", ->
localStorage = window.localStorage
remoteStorage = cy.state("window").localStorage
setStorages = cy.spy Cypress.LocalStorage, "setStorages"
cy.clearLocalStorage().then ->
expect(setStorages).to.be.calledWith localStorage, remoteStorage
it "unsets the storages", ->
unsetStorages = cy.spy Cypress.LocalStorage, "unsetStorages"
cy.clearLocalStorage().then ->
expect(unsetStorages).to.be.called
it "sets subject to remote localStorage", ->
ls = cy.state("window").localStorage
cy.clearLocalStorage().then (remote) ->
expect(remote).to.eq ls
describe "test:before:run", ->
it "clears localStorage before each test run", ->
clear = cy.spy Cypress.LocalStorage, "clear"
Cypress.emit("test:before:run", {})
expect(clear).to.be.calledWith []
describe "errors", ->
it "throws when being passed a non string or regexp", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.include "`cy.clearLocalStorage()` must be called with either a string or regular expression."
expect(err.docsUrl).to.include("https://on.cypress.io/clearlocalstorage")
done()
# A number is used as an object will be considered as `options`
cy.clearLocalStorage(1)
describe ".log", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
@lastLog = log
return null
it "ends immediately", ->
cy.clearLocalStorage().then ->
lastLog = @lastLog
expect(lastLog.get("ended")).to.be.true
expect(lastLog.get("state")).to.eq("passed")
it "snapshots immediately", ->
cy.clearLocalStorage().then ->
lastLog = @lastLog
expect(lastLog.get("snapshots").length).to.eq(1)
expect(lastLog.get("snapshots")[0]).to.be.an("object")
describe "without log", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
@lastLog = log
return null
it "log is disabled", ->
cy.clearLocalStorage('foo', {log: false}).then ->
lastLog = @lastLog
expect(lastLog).to.be.undefined
it "log is disabled without key", ->
cy.clearLocalStorage({log: false}).then ->
lastLog = @lastLog
expect(lastLog).to.be.undefined

View File

@@ -0,0 +1,118 @@
describe('src/cy/commands/local_storage', () => {
context('#clearLocalStorage', () => {
it('passes keys onto Cypress.LocalStorage.clear', () => {
const clear = cy.spy(Cypress.LocalStorage, 'clear')
cy.clearLocalStorage('foo').then(() => {
expect(clear).to.be.calledWith('foo')
})
})
it('sets the storages', () => {
const {
localStorage,
} = window
const remoteStorage = cy.state('window').localStorage
const setStorages = cy.spy(Cypress.LocalStorage, 'setStorages')
cy.clearLocalStorage().then(() => {
expect(setStorages).to.be.calledWith(localStorage, remoteStorage)
})
})
it('unsets the storages', () => {
const unsetStorages = cy.spy(Cypress.LocalStorage, 'unsetStorages')
cy.clearLocalStorage().then(() => {
expect(unsetStorages).to.be.called
})
})
it('sets subject to remote localStorage', () => {
const ls = cy.state('window').localStorage
cy.clearLocalStorage().then((remote) => {
expect(remote).to.eq(ls)
})
})
describe('test:before:run', () => {
it('clears localStorage before each test run', () => {
const clear = cy.spy(Cypress.LocalStorage, 'clear')
Cypress.emit('test:before:run', {})
expect(clear).to.be.calledWith([])
})
})
describe('errors', () => {
it('throws when being passed a non string or regexp', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.clearLocalStorage()` must be called with either a string or regular expression.')
expect(err.docsUrl).to.include('https://on.cypress.io/clearlocalstorage')
done()
})
// A number is used as an object will be considered as `options`
cy.clearLocalStorage(1)
})
})
describe('.log', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
this.lastLog = log
})
return null
})
it('ends immediately', () => {
cy.clearLocalStorage().then(function () {
const { lastLog } = this
expect(lastLog.get('ended')).to.be.true
expect(lastLog.get('state')).to.eq('passed')
})
})
it('snapshots immediately', () => {
cy.clearLocalStorage().then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
})
describe('without log', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
this.lastLog = log
})
return null
})
it('log is disabled', () => {
cy.clearLocalStorage('foo', { log: false }).then(function () {
const { lastLog } = this
expect(lastLog).to.be.undefined
})
})
it('log is disabled without key', () => {
cy.clearLocalStorage({ log: false }).then(function () {
const { lastLog } = this
expect(lastLog).to.be.undefined
})
})
})
})
})

View File

@@ -1,396 +0,0 @@
_ = Cypress._
$ = Cypress.$
describe "src/cy/commands/location", ->
beforeEach ->
cy.visit("/fixtures/generic.html")
context "#url", ->
it "returns the location href", ->
cy.url().then (url) ->
expect(url).to.eq "http://localhost:3500/fixtures/generic.html"
it "eventually resolves", ->
_.delay =>
win = cy.state("window")
win.location.href = "/foo/bar/baz.html"
, 100
cy.url().should("match", /baz/).and("eq", "http://localhost:3500/foo/bar/baz.html")
it "catches thrown errors", ->
cy.stub(Cypress.utils, "locToString")
.onFirstCall().throws(new Error)
.onSecondCall().returns("http://localhost:3500/baz.html")
cy.url().should("include", "/baz.html")
describe "assertion verification", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
if log.get("name") is "assert"
@lastLog = log
return null
it "eventually passes the assertion", ->
cy.on "command:retry", _.after 2, _.once =>
win = cy.state("window")
win.location.href = "/foo/bar/baz.html"
cy.url().should("match", /baz/).then ->
lastLog = @lastLog
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("passed")
expect(lastLog.get("ended")).to.be.true
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 100)
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "eventually fails the assertion", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(err.message).to.include(lastLog.get("error").message)
expect(err.message).not.to.include("undefined")
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("error")).to.be.an.instanceof(chai.AssertionError)
done()
cy.url().should("eq", "not-this")
it "does not log an additional log on failure", (done) ->
cy.on "fail", =>
expect(@logs.length).to.eq(2)
done()
cy.url().should("eq", "not-this")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "ends immediately", ->
cy.url().then ->
lastLog = @lastLog
expect(lastLog.get("ended")).to.be.true
expect(lastLog.get("state")).to.eq("passed")
it "snapshots immediately", ->
cy.url().then ->
lastLog = @lastLog
expect(lastLog.get("snapshots").length).to.eq(1)
expect(lastLog.get("snapshots")[0]).to.be.an("object")
it "logs obj", ->
cy.url().then ->
obj = {
name: "url"
message: ""
}
lastLog = @lastLog
_.each obj, (value, key) =>
expect(lastLog.get(key)).to.deep.eq value
it "does not emit when {log: false}", ->
cy.url({log: false}).then ->
expect(@log).to.be.undefined
it "#consoleProps", ->
cy.url().then ->
consoleProps = @lastLog.invoke("consoleProps")
expect(consoleProps).to.deep.eq {
Command: "url"
Yielded: "http://localhost:3500/fixtures/generic.html"
}
context "#hash", ->
it "returns the location hash", ->
cy.hash().then (hash) ->
expect(hash).to.eq ""
it "eventually resolves", ->
_.delay ->
win = cy.state("window")
win.location.hash = "users/1"
, 100
cy.hash().should("match", /users/).and("eq", "#users/1")
describe "assertion verification", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
if log.get("name") is "assert"
@lastLog = log
return null
it "eventually passes the assertion", ->
cy.on "command:retry", _.after 2, =>
win = cy.state("window")
win.location.hash = "users/1"
cy.hash().should("match", /users/).then ->
lastLog = @lastLog
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("passed")
expect(lastLog.get("ended")).to.be.true
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 100)
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "eventually fails the assertion", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(err.message).to.include(lastLog.get("error").message)
expect(err.message).not.to.include("undefined")
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("error")).to.be.an.instanceof(chai.AssertionError)
done()
cy.hash().should("eq", "not-this")
it "does not log an additional log on failure", (done) ->
cy.on "fail", =>
expect(@logs.length).to.eq(2)
done()
cy.hash().should("eq", "not-this")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "ends immediately", ->
cy.hash().then ->
lastLog = @lastLog
expect(lastLog.get("ended")).to.be.true
expect(lastLog.get("state")).to.eq("passed")
it "snapshots immediately", ->
cy.hash().then ->
lastLog = @lastLog
expect(lastLog.get("snapshots").length).to.eq(1)
expect(lastLog.get("snapshots")[0]).to.be.an("object")
it "logs obj", ->
cy.hash().then ->
obj = {
name: "hash"
message: ""
}
lastLog = @lastLog
_.each obj, (value, key) =>
expect(lastLog.get(key)).to.deep.eq value
it "does not emit when {log: false}", ->
cy.hash({log: false}).then ->
expect(@log).to.be.undefined
it "#consoleProps", ->
cy.hash().then ->
consoleProps = @lastLog.invoke("consoleProps")
expect(consoleProps).to.deep.eq {
Command: "hash"
Yielded: ""
}
context "#location", ->
it "returns the location object", ->
cy.location().then (loc) ->
expect(loc).to.have.keys ["auth", "authObj", "hash", "href", "host", "hostname", "origin", "pathname", "port", "protocol", "search", "originPolicy", "superDomain", "toString"]
it "returns a specific key from location object", ->
cy.location("href").then (href) ->
expect(href).to.eq "http://localhost:3500/fixtures/generic.html"
it "eventually resolves", ->
_.delay ->
win = cy.state("window")
win.location.pathname = "users/1"
, 100
cy.location().should("have.property", "pathname").and("match", /users/)
describe "assertion verification", ->
beforeEach ->
cy.on "log:added", (attrs, log) =>
if log.get("name") is "assert"
@lastLog = log
return null
it "eventually passes the assertion", ->
cy.on "command:retry", _.after 2, _.once =>
win = cy.state("window")
win.location.pathname = "users/1"
cy.location("pathname").should("match", /users/).then ->
lastLog = @lastLog
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("passed")
expect(lastLog.get("ended")).to.be.true
describe "errors", ->
beforeEach ->
Cypress.config("defaultCommandTimeout", 100)
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "throws when passed a non-existent key", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(err.message).to.include(lastLog.get("error").message)
expect(err.message).to.include("Location object does not have key: `ladida`")
expect(err.docsUrl).to.include("https://on.cypress.io/location")
expect(lastLog.get("name")).to.eq("location")
expect(lastLog.get("state")).to.eq("failed")
done()
cy.location('ladida')
it "eventually fails the assertion", (done) ->
cy.on "fail", (err) =>
lastLog = @lastLog
expect(err.message).to.include(lastLog.get("error").message)
expect(err.message).not.to.include("undefined")
expect(lastLog.get("name")).to.eq("assert")
expect(lastLog.get("state")).to.eq("failed")
expect(lastLog.get("error")).to.be.an.instanceof(chai.AssertionError)
done()
cy.location("pathname").should("eq", "not-this")
it "does not log an additional log on failure", (done) ->
logs = []
cy.on "log:added", (attrs, log) ->
logs.push(log)
cy.on "fail", =>
expect(@logs.length).to.eq(2)
done()
cy.location("pathname").should("eq", "not-this")
describe ".log", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@lastLog = log
@logs.push(log)
return null
it "ends immediately", ->
cy.location("href").then ->
lastLog = @lastLog
expect(lastLog.get("ended")).to.be.true
expect(lastLog.get("state")).to.eq("passed")
it "snapshots immediately", ->
cy.location("href").then ->
lastLog = @lastLog
expect(lastLog.get("snapshots").length).to.eq(1)
expect(lastLog.get("snapshots")[0]).to.be.an("object")
it "does not emit when {log: false} as options", ->
cy.location("href", {log: false}).then ->
expect(@log).to.be.undefined
it "does not emit when {log: false} as key", ->
cy.location({log: false}).then ->
expect(@log).to.be.undefined
it "logs obj without a message", ->
cy.location().then ->
obj = {
name: "location"
message: ""
}
lastLog = @lastLog
_.each obj, (value, key) =>
expect(lastLog.get(key)).to.deep.eq value
it "logs obj with a message", ->
cy.location("origin").then ->
obj = {
name: "location"
message: "origin"
}
lastLog = @lastLog
_.each obj, (value, key) =>
expect(lastLog.get(key)).to.deep.eq value
it "#consoleProps", ->
cy.location().then ->
consoleProps = @lastLog.invoke("consoleProps")
expect(_.keys(consoleProps)).to.deep.eq ["Command", "Yielded"]
expect(consoleProps.Command).to.eq "location"
expect(_.keys(consoleProps.Yielded)).to.deep.eq ["auth", "authObj", "hash", "href", "host", "hostname", "origin", "pathname", "port", "protocol", "search", "originPolicy", "superDomain", "toString"]

View File

@@ -0,0 +1,513 @@
const { _ } = Cypress
describe('src/cy/commands/location', () => {
beforeEach(() => {
cy.visit('/fixtures/generic.html')
})
context('#url', () => {
it('returns the location href', () => {
cy.url().then((url) => {
expect(url).to.eq('http://localhost:3500/fixtures/generic.html')
})
})
it('eventually resolves', () => {
_.delay(() => {
const win = cy.state('window')
win.location.href = '/foo/bar/baz.html'
}, 100)
cy.url().should('match', /baz/).and('eq', 'http://localhost:3500/foo/bar/baz.html')
})
it('catches thrown errors', () => {
cy.stub(Cypress.utils, 'locToString')
.onFirstCall().throws(new Error)
.onSecondCall().returns('http://localhost:3500/baz.html')
cy.url().should('include', '/baz.html')
})
describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
cy.on('command:retry', _.after(2, _.once(() => {
const win = cy.state('window')
win.location.href = '/foo/bar/baz.html'
})))
cy.url().should('match', /baz/).then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('passed')
expect(lastLog.get('ended')).to.be.true
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 100)
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('eventually fails the assertion', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(err.message).to.include(lastLog.get('error').message)
expect(err.message).not.to.include('undefined')
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
done()
})
cy.url().should('eq', 'not-this')
})
it('does not log an additional log on failure', function (done) {
cy.on('fail', () => {
expect(this.logs.length).to.eq(2)
done()
})
cy.url().should('eq', 'not-this')
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('ends immediately', () => {
cy.url().then(function () {
const { lastLog } = this
expect(lastLog.get('ended')).to.be.true
expect(lastLog.get('state')).to.eq('passed')
})
})
it('snapshots immediately', () => {
cy.url().then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
it('logs obj', () => {
cy.url().then(function () {
const obj = {
name: 'url',
message: '',
}
const { lastLog } = this
_.each(obj, (value, key) => {
expect(lastLog.get(key)).to.deep.eq(value)
})
})
})
it('does not emit when {log: false}', () => {
cy.url({ log: false }).then(function () {
expect(this.log).to.be.undefined
})
})
it('#consoleProps', () => {
cy.url().then(function () {
const consoleProps = this.lastLog.invoke('consoleProps')
expect(consoleProps).to.deep.eq({
Command: 'url',
Yielded: 'http://localhost:3500/fixtures/generic.html',
})
})
})
})
})
context('#hash', () => {
it('returns the location hash', () => {
cy.hash().then((hash) => {
expect(hash).to.eq('')
})
})
it('eventually resolves', () => {
_.delay(() => {
const win = cy.state('window')
win.location.hash = 'users/1'
}, 100)
cy.hash().should('match', /users/).and('eq', '#users/1')
})
describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
cy.on('command:retry', _.after(2, () => {
const win = cy.state('window')
win.location.hash = 'users/1'
}))
cy.hash().should('match', /users/).then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('passed')
expect(lastLog.get('ended')).to.be.true
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 100)
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('eventually fails the assertion', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(err.message).to.include(lastLog.get('error').message)
expect(err.message).not.to.include('undefined')
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
done()
})
cy.hash().should('eq', 'not-this')
})
it('does not log an additional log on failure', function (done) {
cy.on('fail', () => {
expect(this.logs.length).to.eq(2)
done()
})
cy.hash().should('eq', 'not-this')
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('ends immediately', () => {
cy.hash().then(function () {
const { lastLog } = this
expect(lastLog.get('ended')).to.be.true
expect(lastLog.get('state')).to.eq('passed')
})
})
it('snapshots immediately', () => {
cy.hash().then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
it('logs obj', () => {
cy.hash().then(function () {
const obj = {
name: 'hash',
message: '',
}
const { lastLog } = this
_.each(obj, (value, key) => {
expect(lastLog.get(key)).to.deep.eq(value)
})
})
})
it('does not emit when {log: false}', () => {
cy.hash({ log: false }).then(function () {
expect(this.log).to.be.undefined
})
})
it('#consoleProps', () => {
cy.hash().then(function () {
const consoleProps = this.lastLog.invoke('consoleProps')
expect(consoleProps).to.deep.eq({
Command: 'hash',
Yielded: '',
})
})
})
})
})
context('#location', () => {
it('returns the location object', () => {
cy.location().then((loc) => {
expect(loc).to.have.keys(['auth', 'authObj', 'hash', 'href', 'host', 'hostname', 'origin', 'pathname', 'port', 'protocol', 'search', 'originPolicy', 'superDomain', 'toString'])
})
})
it('returns a specific key from location object', () => {
cy.location('href').then((href) => {
expect(href).to.eq('http://localhost:3500/fixtures/generic.html')
})
})
it('eventually resolves', () => {
_.delay(() => {
const win = cy.state('window')
win.location.pathname = 'users/1'
}, 100)
cy.location().should('have.property', 'pathname').and('match', /users/)
})
describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
cy.on('command:retry', _.after(2, _.once(() => {
const win = cy.state('window')
win.location.pathname = 'users/1'
})))
cy.location('pathname').should('match', /users/).then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('passed')
expect(lastLog.get('ended')).to.be.true
})
})
})
describe('errors', () => {
beforeEach(function () {
Cypress.config('defaultCommandTimeout', 100)
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('throws when passed a non-existent key', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(err.message).to.include(lastLog.get('error').message)
expect(err.message).to.include('Location object does not have key: `ladida`')
expect(err.docsUrl).to.include('https://on.cypress.io/location')
expect(lastLog.get('name')).to.eq('location')
expect(lastLog.get('state')).to.eq('failed')
done()
})
cy.location('ladida')
})
it('eventually fails the assertion', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(err.message).to.include(lastLog.get('error').message)
expect(err.message).not.to.include('undefined')
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
done()
})
cy.location('pathname').should('eq', 'not-this')
})
it('does not log an additional log on failure', function (done) {
const logs = []
cy.on('log:added', (attrs, log) => {
logs.push(log)
})
cy.on('fail', () => {
expect(this.logs.length).to.eq(2)
done()
})
cy.location('pathname').should('eq', 'not-this')
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('ends immediately', () => {
cy.location('href').then(function () {
const { lastLog } = this
expect(lastLog.get('ended')).to.be.true
expect(lastLog.get('state')).to.eq('passed')
})
})
it('snapshots immediately', () => {
cy.location('href').then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
it('does not emit when {log: false} as options', () => {
cy.location('href', { log: false }).then(function () {
expect(this.log).to.be.undefined
})
})
it('does not emit when {log: false} as key', () => {
cy.location({ log: false }).then(function () {
expect(this.log).to.be.undefined
})
})
it('logs obj without a message', () => {
cy.location().then(function () {
const obj = {
name: 'location',
message: '',
}
const { lastLog } = this
_.each(obj, (value, key) => {
expect(lastLog.get(key)).to.deep.eq(value)
})
})
})
it('logs obj with a message', () => {
cy.location('origin').then(function () {
const obj = {
name: 'location',
message: 'origin',
}
const { lastLog } = this
_.each(obj, (value, key) => {
expect(lastLog.get(key)).to.deep.eq(value)
})
})
})
it('#consoleProps', () => {
cy.location().then(function () {
const consoleProps = this.lastLog.invoke('consoleProps')
expect(_.keys(consoleProps)).to.deep.eq(['Command', 'Yielded'])
expect(consoleProps.Command).to.eq('location')
expect(_.keys(consoleProps.Yielded)).to.deep.eq(['auth', 'authObj', 'hash', 'href', 'host', 'hostname', 'origin', 'pathname', 'port', 'protocol', 'search', 'originPolicy', 'superDomain', 'toString'])
})
})
})
})
})