move from driver/cypress/integration to driver/cypress/e2e

This commit is contained in:
Emily Rohrbough
2022-01-21 15:59:20 -06:00
parent f669c101e1
commit a33288b4a6
10 changed files with 44 additions and 1898 deletions

View File

@@ -20,7 +20,7 @@ function getFileContents (subject) {
describe('src/cy/commands/actions/selectFile', () => {
beforeEach(() => {
cy.visit('/fixtures/files-form.html')
cy.wrap(Buffer.from('foo')).as('foo')
cy.wrap(Cypress.Buffer.from('foo')).as('foo')
})
context('#selectFile', () => {
@@ -32,7 +32,7 @@ describe('src/cy/commands/actions/selectFile', () => {
.then((input) => {
expect(input[0].files.length).to.eq(1)
expect(input[0].files[0].name).to.eq('foo.txt')
expect(input[0].files[0].type).to.eq('')
expect(input[0].files[0].type).to.eq('text/plain')
expect(input[0].files[0].lastModified).to.be.closeTo(Date.now(), 1000)
})
@@ -50,10 +50,12 @@ describe('src/cy/commands/actions/selectFile', () => {
contents: '@foo',
fileName: 'foo.txt',
}, {
contents: Buffer.from('{"a":"bar"}'),
contents: Cypress.Buffer.from('{"a":"bar"}'),
fileName: 'bar.json',
},
Buffer.from('baz'),
Cypress.Buffer.from('baz'),
// 'baz' in ascii
Uint8Array.from([98, 97, 122]),
])
cy.get('#multiple')
@@ -62,6 +64,7 @@ describe('src/cy/commands/actions/selectFile', () => {
expect(input[0].files[0].name).to.eq('foo.txt')
expect(input[0].files[1].name).to.eq('bar.json')
expect(input[0].files[2].name).to.eq('')
expect(input[0].files[3].name).to.eq('')
})
cy.get('#multiple')
@@ -70,6 +73,7 @@ describe('src/cy/commands/actions/selectFile', () => {
expect(contents[0]).to.eq('foo')
expect(contents[1]).to.eq('{"a":"bar"}')
expect(contents[2]).to.eq('baz')
expect(contents[3]).to.eq('baz')
})
})
@@ -251,6 +255,40 @@ describe('src/cy/commands/actions/selectFile', () => {
})
})
describe('mime types', () => {
it('uses empty string for unknown extensions', () => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: 'foo.barbaz' })
.then((input) => {
expect(input[0].files[0].type).to.eq('')
})
})
it('works with several common extensions', () => {
[
['png', 'image/png'],
['jpg', 'image/jpeg'],
['zip', 'application/zip'],
['yaml', 'text/yaml'],
['json', 'application/json'],
].forEach(([extension, mimeType]) => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: `foo.${extension}` })
.then((input) => {
expect(input[0].files[0].type).to.eq(mimeType)
})
})
})
it('allows users to specify a mimetype', () => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: 'foo.zip', mimeType: 'image/png' })
.then((input) => {
expect(input[0].files[0].type).to.eq('image/png')
})
})
})
describe('errors', {
defaultCommandTimeout: 50,
}, () => {

View File

@@ -3,8 +3,8 @@ describe('selector_playground', () => {
cy.visit('/fixtures/dom.html')
.then(() => {
// We trick the selector-playground into rendering while the test is running
top.UnifiedRunner.MobX.configure({ enforceActions: 'never' })
top.__showSelectorPlaygroundForTestingPurposes()
top.Runner.configureMobx({ enforceActions: 'never' })
top.Runner.state.isRunning = false
const $highlightBtn = cy.$$('button.highlight-toggle:visible', top.document)

View File

@@ -1,713 +0,0 @@
const { _, $ } = Cypress
// Reading and decoding files from an input element would, in the real world,
// be handled by the application under test, and they would assert on their
// application state. We want to assert on how selectFile behaves directly
// though, and getting the files associated with an <input> as strings is
// handy.
function getFileContents (subject) {
const decoder = new TextDecoder('utf8')
const fileContents = _.map(subject[0].files, (f) => {
return f
.arrayBuffer()
.then((c) => decoder.decode(c))
})
return Promise.all(fileContents)
}
describe('src/cy/commands/actions/selectFile', () => {
beforeEach(() => {
cy.visit('/fixtures/files-form.html')
cy.wrap(Cypress.Buffer.from('foo')).as('foo')
})
context('#selectFile', () => {
it('selects a single file', () => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: 'foo.txt' })
cy.get('#basic')
.then((input) => {
expect(input[0].files.length).to.eq(1)
expect(input[0].files[0].name).to.eq('foo.txt')
expect(input[0].files[0].type).to.eq('text/plain')
expect(input[0].files[0].lastModified).to.be.closeTo(Date.now(), 1000)
})
cy.get('#basic')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
it('selects multiple files', () => {
cy.get('#multiple')
.selectFile([
{
contents: '@foo',
fileName: 'foo.txt',
}, {
contents: Cypress.Buffer.from('{"a":"bar"}'),
fileName: 'bar.json',
},
Cypress.Buffer.from('baz'),
// 'baz' in ascii
Uint8Array.from([98, 97, 122]),
])
cy.get('#multiple')
.should('include.value', 'foo.txt')
.then((input) => {
expect(input[0].files[0].name).to.eq('foo.txt')
expect(input[0].files[1].name).to.eq('bar.json')
expect(input[0].files[2].name).to.eq('')
expect(input[0].files[3].name).to.eq('')
})
cy.get('#multiple')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eq('foo')
expect(contents[1]).to.eq('{"a":"bar"}')
expect(contents[2]).to.eq('baz')
expect(contents[3]).to.eq('baz')
})
})
it('allows custom lastModified', () => {
cy.get('#basic').selectFile({
contents: '@foo',
lastModified: 1234,
})
cy.get('#basic').then((input) => {
expect(input[0].files[0].lastModified).to.eq(1234)
})
})
it('selects files with an input from a label', () => {
cy.get('#basic-label').selectFile({ contents: '@foo' })
cy.get('#basic')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
it('selects files with an input from a containing label', () => {
cy.get('#containing-label').selectFile({ contents: '@foo' })
cy.get('#contained')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
it('invokes change and input events on the input', (done) => {
const $input = cy.$$('#basic')
$input.on('input', (e) => {
const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'composed', 'target', 'type')
expect(obj).to.deep.eq({
bubbles: true,
cancelable: false,
composed: true,
target: $input.get(0),
type: 'input',
})
$input.on('change', (e) => {
const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'composed', 'target', 'type')
expect(obj).to.deep.eq({
bubbles: true,
cancelable: false,
composed: false,
target: $input.get(0),
type: 'change',
})
done()
})
})
cy.get('#basic').selectFile({ contents: '@foo' })
})
it('bubbles events', (done) => {
cy.window().then((win) => {
$(win).on('input', () => {
done()
})
})
cy.get('#basic').selectFile({ contents: '@foo' })
})
it('invokes events on the input without changing subject when passed a label', (done) => {
cy.$$('#basic-label').on('input', () => {
throw new Error('shouldn\'t happen')
})
cy.$$('#basic').on('input', () => {
done()
})
cy.get('#basic-label').selectFile({ contents: '@foo' })
.should('have.id', 'basic-label')
})
it('can empty previously filled input', () => {
cy.get('#basic').selectFile({ contents: '@foo' })
cy.get('#basic').selectFile([])
.then((input) => {
expect(input[0].files.length).to.eq(0)
})
})
it('works with shadow DOMs', () => {
cy.get('#shadow')
.shadow()
.find('input')
.as('shadowInput')
.selectFile('@foo')
cy.get('@shadowInput')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
describe('shorthands', () => {
const validJsonString = `{
"foo": 1,
"bar": {
"baz": "cypress"
}
}
`
it('works with aliased strings', () => {
cy.wrap('foobar').as('alias')
cy.get('#basic').selectFile('@alias')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foobar')
})
})
it('works with aliased objects', () => {
cy.wrap({ foo: 'bar' }).as('alias')
cy.get('#basic').selectFile('@alias')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('{"foo":"bar"}')
})
})
it('works with aliased fixtures', () => {
cy.fixture('valid.json').as('myFixture')
cy.get('#basic').selectFile('@myFixture')
.then(getFileContents)
.then((contents) => {
// Because json files are loaded as objects, they get reencoded before
// being used, stripping spaces and newlines
expect(contents[0]).to.eql('{"foo":1,"bar":{"baz":"cypress"}}')
})
})
// Because this is such an important recipe for users, it gets a separate test
// even though readFile already has unit tests around reading files as buffers.
it('works with files read with null encoding', () => {
cy.readFile('cypress/fixtures/valid.json', { encoding: null }).as('myFile')
cy.get('#basic').selectFile('@myFile')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql(validJsonString)
})
})
it('works with passed in paths', () => {
cy.get('#multiple').selectFile(['cypress/fixtures/valid.json', 'cypress/fixtures/app.js'])
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql(validJsonString)
expect(contents[1]).to.eql('{ \'bar\' }\n')
})
cy.get('#multiple')
.should('include.value', 'valid.json')
.then((input) => {
expect(input[0].files[0].name).to.eq('valid.json')
expect(input[0].files[1].name).to.eq('app.js')
})
})
})
describe('mime types', () => {
it('uses empty string for unknown extensions', () => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: 'foo.barbaz' })
.then((input) => {
expect(input[0].files[0].type).to.eq('')
})
})
it('works with several common extensions', () => {
[
['png', 'image/png'],
['jpg', 'image/jpeg'],
['zip', 'application/zip'],
['yaml', 'text/yaml'],
['json', 'application/json'],
].forEach(([extension, mimeType]) => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: `foo.${extension}` })
.then((input) => {
expect(input[0].files[0].type).to.eq(mimeType)
})
})
})
it('allows users to specify a mimetype', () => {
cy.get('#basic')
.selectFile({ contents: '@foo', fileName: 'foo.zip', mimeType: 'image/png' })
.then((input) => {
expect(input[0].files[0].type).to.eq('image/png')
})
})
})
describe('errors', {
defaultCommandTimeout: 50,
}, () => {
it('is a child command', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('A child command must be chained after a parent because it operates on a previous subject.')
done()
})
cy.selectFile({ contents: '@foo' })
})
it('throws when non dom subject', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` failed because it requires a DOM element.')
done()
})
cy.noop({}).selectFile({ contents: '@foo' })
})
it('throws when non-input subject', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<body>...</body>`')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('body').selectFile({ contents: '@foo' })
})
it('throws when non-file input', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<input type="text" id="text-input">`')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#text-input').selectFile({ contents: '@foo' })
})
it('throws when label for non-file input', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<label for="text-input" id="text-label">Text label</label>`')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#text-label').selectFile({ contents: '@foo' })
})
it('throws when label without an attached input', function (done) {
// Even though this label contains a file input, testing on real browsers confirms that clicking it
// does *not* activate the contained input.
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<label for="nonexistent" id="nonexistent-label">...</label>`')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#nonexistent-label').selectFile({ contents: '@foo' })
})
it('throws when subject is collection of elements', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only be called on a single element. Your subject contained')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('input[type="file"]').selectFile({ contents: '@foo' })
})
it('throws when no arguments given', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` must be passed a Buffer or an object with a non-null `contents` property as its 1st argument. You passed: `undefined`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile()
})
it('throws when file is null', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` must be passed a Buffer or an object with a non-null `contents` property as its 1st argument. You passed: `null`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile(null)
})
it('throws when single file.contents is null', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` must be passed a Buffer or an object with a non-null `contents` property as its 1st argument. You passed: `{"contents":null}`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile({ contents: null })
})
it('throws when file is an unknown alias', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` could not find a registered alias for: `@unknown`.')
done()
})
cy.get('#basic').selectFile('@unknown')
})
it('throws when file is an alias for a DOM node', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only attach strings, Buffers or objects, while your alias `@body` resolved to: `<body>...</body>`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('body').as('body')
cy.get('#basic').selectFile('@body')
})
it('throws when file is an alias for null', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only attach strings, Buffers or objects, while your alias `@null` resolved to: `null`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.wrap(null).as('null')
cy.get('#basic').selectFile('@null')
})
it('throws with aliased intercepts', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` can only attach strings, Buffers or objects, while your alias `@postUser` resolved to: `null`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.intercept('POST', '/users', {}).as('postUser')
cy.get('#basic').selectFile('@postUser')
})
it('throws when any path does not exist', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile("this/file/doesNotExist.json")` failed because the file does not exist at the following path:')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile(['cypress/fixtures/valid.json', 'this/file/doesNotExist.json'])
})
it('throws when any file\'s contents is undefined', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` must be passed an array of Buffers or objects with non-null `contents`. At files[1] you passed: `{}`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile([{ contents: '@foo' }, {}])
})
it('throws on invalid action', function (done) {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` `action` can only be `select` or `drag-drop`. You passed: `foobar`.')
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
done()
})
cy.get('#basic').selectFile({ contents: '@foo' }, { action: 'foobar' })
})
})
/*
* The tests around actionability are somewhat limited, since the functionality is thoroughly tested in the
* `cy.trigger()` unit tests. We include a few tests directly on `cy.selectFile()` in order to ensure we're
* using $actionability.verify() properly, but don't extensively exercise the logic within it here.
*
* See trigger_spec.js for the full actionability test suite.
*/
describe('actionability', {
defaultCommandTimeout: 50,
}, () => {
it('selects files with a hidden input from a visible label', () => {
cy.get('#hidden-label').selectFile({ contents: '@foo' })
cy.get('#hidden')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
it('does not work on hidden inputs by default', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` failed because this element is not visible')
done()
})
cy.get('#hidden').selectFile({ contents: '@foo' })
})
it('can force on hidden inputs', () => {
cy.get('#hidden').selectFile({ contents: '@foo' }, { force: true })
})
it('does not work on covered inputs by default', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include(`\`<input id="covered" type="file">\`
is being covered by another element:
\`<div id="covering"></div>\``)
done()
})
cy.get('#covered').selectFile({ contents: '@foo' })
})
it('can force on covered inputs', () => {
cy.get('#covered').selectFile({ contents: '@foo' }, { force: true })
})
it('does not work on disabled inputs by default', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` failed because this element is `disabled`')
done()
})
cy.get('#disabled-label').selectFile({ contents: '@foo' })
})
it('can force on disabled inputs', () => {
cy.get('#disabled-label').selectFile({ contents: '@foo' }, { force: true })
})
it('does not work on hidden labels by default', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.selectFile()` failed because this element is not visible')
done()
})
cy.get('#hidden-basic-label').selectFile({ contents: '@foo' })
})
it('can force on hidden labels', () => {
cy.get('#hidden-basic-label').selectFile({ contents: '@foo' }, { force: true })
})
it('can scroll to input', () => {
const scrolled = []
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy.get('#scroll').selectFile({ contents: '@foo' })
.then(() => {
expect(scrolled).not.to.be.empty
})
})
it('can scroll to label', () => {
const scrolled = []
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy.get('#scroll-label').selectFile({ contents: '@foo' })
.then(() => {
expect(scrolled).not.to.be.empty
})
})
it('does not scroll when forced', () => {
const scrolled = []
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy.get('#scroll-label').selectFile({ contents: '@foo' }, { force: true })
.then(() => {
expect(scrolled).to.be.empty
})
})
it('waits until input stops animating', {
defaultCommandTimeout: 1000,
}, () => {
let retries = 0
cy.on('command:retry', (obj) => {
retries += 1
})
cy.stub(cy, 'ensureElementIsNotAnimating')
.throws(new Error('animating!'))
.onThirdCall().returns()
cy.get('#basic').selectFile({ contents: '@foo' }).then(() => {
expect(retries).to.eq(3)
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
})
})
it('can specify scrollBehavior in options', () => {
cy.get('#scroll').then((el) => {
cy.spy(el[0], 'scrollIntoView')
})
cy.get('#scroll').selectFile({ contents: '@foo' }, { scrollBehavior: 'bottom' })
cy.get('#scroll').then((el) => {
expect(el[0].scrollIntoView).to.be.calledWith({ block: 'end' })
})
})
})
describe('drag-drop', () => {
it('attaches a file to an input when targeted', () => {
cy.get('#basic').selectFile({ contents: '@foo' }, { action: 'drag-drop' })
cy.get('#basic')
.then(getFileContents)
.then((contents) => {
expect(contents[0]).to.eql('foo')
})
})
it('invokes change and input events on an input when dropped over', (done) => {
const $input = cy.$$('#basic')
$input.on('input', (e) => {
const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'composed', 'target', 'type')
expect(obj).to.deep.eq({
bubbles: true,
cancelable: false,
composed: true,
target: $input.get(0),
type: 'input',
})
$input.on('change', (e) => {
const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'composed', 'target', 'type')
expect(obj).to.deep.eq({
bubbles: true,
cancelable: false,
composed: false,
target: $input.get(0),
type: 'change',
})
done()
})
})
cy.get('#basic').selectFile({ contents: '@foo' }, { action: 'drag-drop' })
})
it('does not follow labels to their inputs', () => {
cy.get('#basic-label').selectFile({ contents: '@foo' }, { action: 'drag-drop' })
cy.get('#basic').then((input) => {
expect(input[0].files.length).to.eql(0)
})
})
it('does not select multiple files with a single-file input', () => {
cy.get('#basic').selectFile(['@foo', '@foo'], { action: 'drag-drop' })
cy.get('#basic').then((input) => {
expect(input[0].files.length).to.eql(0)
})
})
it('drops files onto any element and triggers events', (done) => {
const $body = cy.$$('body')
let events = []
$body.on('input', (e) => {
throw new Error('should not trigger input')
})
$body.on('change', (e) => {
throw new Error('should not trigger change')
})
$body.on('drag', (e) => events.push(e))
$body.on('dragenter', (e) => events.push(e))
$body.on('dragover', (e) => events.push(e))
$body.on('drop', (e) => {
events.push(e)
expect(_.map(events, 'originalEvent.type')).to.deep.eql(['drag', 'dragenter', 'dragover', 'drop'])
expect(_.every(events, ['originalEvent.bubbles', true])).to.be.true
expect(_.every(events, ['originalEvent.cancelable', true])).to.be.true
expect(_.every(events, ['originalEvent.composed', true])).to.be.true
expect(_.every(events, ['originalEvent.target', $body[0]])).to.be.true
done()
})
cy.get('body').selectFile('@foo', { action: 'drag-drop' })
})
it('includes an entry in `dataTransfer.types`', (done) => {
cy.$$('#multiple').on('drop', (e) => {
expect(e.originalEvent.dataTransfer.types).to.contain('Files')
done()
})
cy.get('#multiple').selectFile({ contents: '@foo' }, { action: 'drag-drop' })
})
})
})
})

View File

@@ -1,78 +0,0 @@
const {
getBackendStaticResponse,
} = require('../../../../src/cy/net-stubbing/static-response-utils')
describe('driver/src/cy/net-stubbing/static-response-utils', () => {
describe('.getBackendStaticResponse', () => {
describe('delay', () => {
it('does not set delay when delayMS is not provided', () => {
const staticResponse = getBackendStaticResponse({})
expect(staticResponse).to.not.have.property('delay')
})
it('sets delay', () => {
const staticResponse = getBackendStaticResponse({ delayMs: 200 })
expect(staticResponse).to.have.property('delay', 200)
})
})
describe('fixtures', () => {
it('does not set fixtures data when fixtures string is not provided', () => {
const staticResponse = getBackendStaticResponse({})
expect(staticResponse).to.not.have.property('fixtures')
})
it('parses fixture string without file encoding', () => {
const staticResponse = getBackendStaticResponse({ fixture: 'file.html' })
expect(staticResponse.fixture).to.deep.equal({
filePath: 'file.html',
encoding: undefined,
})
})
it('parses fixture string with file encoding', () => {
const staticResponse = getBackendStaticResponse({ fixture: 'file.html,utf8' })
expect(staticResponse.fixture).to.deep.equal({
filePath: 'file.html',
encoding: 'utf8',
})
})
it('parses fixture string with file encoding set as null to indicate Buffer', () => {
const staticResponse = getBackendStaticResponse({ fixture: 'file.html,null' })
expect(staticResponse.fixture).to.deep.equal({
filePath: 'file.html',
encoding: null,
})
})
})
describe('body', () => {
it('does not set body when body is undefined', () => {
const staticResponse = getBackendStaticResponse({})
expect(staticResponse).to.not.have.property('body')
})
it('sets body when body is provided as a string', () => {
const staticResponse = getBackendStaticResponse({ body: 'body' })
expect(staticResponse.body).to.eq('body')
})
it('sets body when body is provided as a ArrayBuffer', () => {
const buffer = new ArrayBuffer(8)
const staticResponse = getBackendStaticResponse({ body: buffer })
expect(staticResponse.body).to.be.a('arraybuffer')
expect(staticResponse.body).to.eq(buffer)
})
})
})
})

View File

@@ -1,480 +0,0 @@
import { expect } from 'chai'
describe('Proxy Logging', () => {
const { _ } = Cypress
const url = '/testFlag'
const alias = 'aliasName'
function testFlag (expectStatus, expectInterceptions, setupFn, getFn) {
return () => {
setupFn()
let resolve
const p = new Promise((_resolve) => resolve = _resolve)
function testLog (log) {
expect(log.alias).to.eq(expectInterceptions.length ? alias : undefined)
expect(log.renderProps).to.deep.include({
interceptions: expectInterceptions,
...(expectStatus ? { status: expectStatus } : {}),
})
resolve()
}
cy.then(() => {
cy.on('log:changed', (log) => {
if (['request', 'xhr'].includes(log.name)) {
try {
testLog(log)
resolve()
} catch (err) {
// eslint-disable-next-line no-console
console.error('assertions failed:', err)
}
}
})
getFn(url)
}).then(() => p)
if (expectStatus) {
cy.wait(`@${alias}`)
}
}
}
context('request logging', () => {
it('fetch log shows resource type, url, method, and status code and has expected snapshots and consoleProps', (done) => {
fetch('/some-url')
// trigger: Cypress.Log() called
cy.once('log:added', (log) => {
expect(log.snapshots).to.be.undefined
expect(log.displayName).to.eq('fetch')
expect(log.renderProps).to.include({
indicator: 'pending',
message: 'GET /some-url',
})
expect(log.consoleProps).to.include({
Method: 'GET',
'Resource Type': 'fetch',
'Request went to origin?': 'yes',
'URL': 'http://localhost:3500/some-url',
})
// case depends on browser
const refererKey = _.keys(log.consoleProps['Request Headers']).find((k) => k.toLowerCase() === 'referer') || 'referer'
expect(log.consoleProps['Request Headers']).to.include({
[refererKey]: window.location.href,
})
expect(log.consoleProps).to.not.have.property('Response Headers')
expect(log.consoleProps).to.not.have.property('Matched `cy.intercept()`')
// trigger: .snapshot('request')
cy.on('log:changed', (log) => {
try {
expect(log.snapshots.map((v) => v.name)).to.deep.eq(['request', 'response'])
expect(log.consoleProps['Response Headers']).to.include({
'x-powered-by': 'Express',
})
expect(log.consoleProps).to.not.have.property('Matched `cy.intercept()`')
expect(log.renderProps).to.include({
indicator: 'bad',
message: 'GET 404 /some-url',
})
expect(Object.keys(log.consoleProps)).to.deep.eq(
['Event', 'Resource Type', 'Method', 'URL', 'Request went to origin?', 'Request Headers', 'Response Status Code', 'Response Headers'],
)
done()
} catch (err) {
// eslint-disable-next-line no-console
console.log('assertion error, retrying', err)
}
})
})
})
// @see https://github.com/cypress-io/cypress/issues/17656
it('xhr log has response body/status code', (done) => {
cy.window()
.then({ timeout: 10000 }, (win) => {
cy.on('log:changed', (log) => {
try {
expect(log.snapshots.map((v) => v.name)).to.deep.eq(['request', 'response'])
expect(log.consoleProps['Response Headers']).to.include({
'x-powered-by': 'Express',
})
expect(log.consoleProps['Response Body']).to.include('Cannot GET /some-url')
expect(log.consoleProps['Response Status Code']).to.eq(404)
expect(log.renderProps).to.include({
indicator: 'bad',
message: 'GET 404 /some-url',
})
expect(Object.keys(log.consoleProps)).to.deep.eq(
['Event', 'Resource Type', 'Method', 'URL', 'Request went to origin?', 'XHR', 'groups', 'Request Headers', 'Response Status Code', 'Response Headers', 'Response Body'],
)
done()
} catch (err) {
// eslint-disable-next-line no-console
console.log('assertion error, retrying', err)
}
})
const xhr = new win.XMLHttpRequest()
xhr.open('GET', '/some-url')
xhr.send()
})
})
it('does not log an unintercepted non-xhr/fetch request', (done) => {
const img = new Image()
const logs: any[] = []
let imgLoaded = false
cy.on('log:added', (log) => {
if (imgLoaded) return
logs.push(log)
})
img.onload = () => {
imgLoaded = true
expect(logs).to.have.length(0)
done()
}
img.src = `/fixtures/media/cypress.png?${Date.now()}`
})
context('with cy.intercept()', () => {
it('shows non-xhr/fetch log if intercepted', (done) => {
const src = `/fixtures/media/cypress.png?${Date.now()}`
cy.intercept('/fixtures/**/*.png*')
.then(() => {
cy.once('log:added', (log) => {
expect(log.displayName).to.eq('image')
expect(log.renderProps).to.include({
indicator: 'pending',
message: `GET ${src}`,
})
done()
})
const img = new Image()
img.src = src
})
})
it('shows cy.visit if intercepted', () => {
cy.intercept('/fixtures/empty.html')
.then(() => {
// trigger: cy.visit()
cy.once('log:added', (log) => {
expect(log.name).to.eq('visit')
// trigger: intercept Cypress.Log
cy.once('log:added', (log) => {
expect(log.displayName).to.eq('document')
})
})
})
.visit('/fixtures/empty.html')
})
it('intercept log has consoleProps with intercept info', (done) => {
cy.intercept('/some-url', 'stubbed response').as('alias')
.then(() => {
fetch('/some-url')
})
cy.on('log:changed', (log) => {
if (log.displayName !== 'fetch') return
try {
expect(log.renderProps).to.deep.include({
message: 'GET 200 /some-url',
indicator: 'successful',
status: undefined,
interceptions: [{
alias: 'alias',
command: 'intercept',
type: 'stub',
}],
})
expect(Object.keys(log.consoleProps)).to.deep.eq(
['Event', 'Resource Type', 'Method', 'URL', 'Request went to origin?', 'Matched `cy.intercept()`', 'Request Headers', 'Response Status Code', 'Response Headers', 'Response Body'],
)
const interceptProps = log.consoleProps['Matched `cy.intercept()`']
expect(interceptProps).to.deep.eq({
Alias: 'alias',
Request: {
method: 'GET',
url: 'http://localhost:3500/some-url',
body: '',
httpVersion: '1.1',
responseTimeout: Cypress.config('responseTimeout'),
headers: interceptProps.Request.headers,
},
Response: {
body: 'stubbed response',
statusCode: 200,
url: 'http://localhost:3500/some-url',
headers: interceptProps.Response.headers,
},
RouteMatcher: {
url: '/some-url',
},
RouteHandler: 'stubbed response',
'RouteHandler Type': 'StaticResponse stub',
})
done()
} catch (err) {
// eslint-disable-next-line no-console
console.error('assertion failed:', err)
}
})
})
it('works with forceNetworkError', () => {
const logs: any[] = []
cy.on('log:added', (log) => {
if (log.displayName === 'fetch') {
logs.push(log)
}
})
cy.intercept('/foo', { forceNetworkError: true }).as('alias')
.then(() => {
return fetch('/foo')
.catch(() => {})
})
.wrap(logs)
.should((logs) => {
// retries...
expect(logs).to.have.length.greaterThan(0)
for (const log of logs) {
expect(log.err).to.include({ name: 'Error' })
expect(log.consoleProps['Error']).to.be.an('Error')
expect(log.snapshots.map((v) => v.name)).to.deep.eq(['request', 'error'])
expect(log.state).to.eq('failed')
}
})
})
context('flags', () => {
const testFlagFetch = (expectStatus, expectInterceptions, setupFn) => {
return testFlag(expectStatus, expectInterceptions, setupFn, (url) => fetch(url))
}
it('is unflagged when not intercepted', testFlagFetch(
undefined,
[],
() => {},
))
it('spied flagged as expected', testFlagFetch(
undefined,
[{
command: 'intercept',
alias,
type: 'spy',
}],
() => {
cy.intercept(url).as(alias)
},
))
it('spy function flagged as expected', testFlagFetch(
undefined,
[{
command: 'intercept',
alias,
type: 'function',
}],
() => {
cy.intercept(url, () => {}).as(alias)
},
))
it('stubbed flagged as expected', testFlagFetch(
undefined,
[{
command: 'intercept',
alias,
type: 'stub',
}],
() => {
cy.intercept(url, 'stubbed response').as(alias)
},
))
it('stubbed flagged as expected with req.reply', testFlagFetch(
undefined,
[{
command: 'intercept',
alias,
type: 'function',
}],
() => {
cy.intercept(url, (req) => {
req.headers.foo = 'bar'
req.reply('stubby mc stub')
}).as(alias)
},
))
it('req modified flagged as expected', testFlagFetch(
'req modified',
[{
command: 'intercept',
alias,
type: 'function',
}],
() => {
cy.intercept(url, (req) => {
req.headers.foo = 'bar'
}).as(alias)
},
))
it('res modified flagged as expected', testFlagFetch(
'res modified',
[{
command: 'intercept',
alias,
type: 'function',
}],
() => {
cy.intercept(url, (req) => {
req.continue((res) => {
res.headers.foo = 'bar'
})
}).as(alias)
},
))
it('req + res modified flagged as expected', testFlagFetch(
'req + res modified',
[{
command: 'intercept',
alias,
type: 'function',
}],
() => {
cy.intercept(url, (req) => {
req.headers.foo = 'bar'
req.continue((res) => {
res.headers.foo = 'bar'
})
}).as(alias)
},
))
})
})
context('with cy.route()', () => {
context('flags', () => {
let $XMLHttpRequest
const testFlagXhr = (expectStatus, expectInterceptions, setupFn) => {
return testFlag(expectStatus, expectInterceptions, setupFn, (url) => {
const xhr = new $XMLHttpRequest()
xhr.open('GET', url)
xhr.send()
})
}
beforeEach(() => {
cy.window()
.then(({ XMLHttpRequest }) => {
$XMLHttpRequest = XMLHttpRequest
})
})
it('is unflagged when not routed', testFlagXhr(
undefined,
[],
() => {},
))
it('spied flagged as expected', testFlagXhr(
undefined,
[{
command: 'route',
alias,
type: 'spy',
}],
() => {
cy.server()
cy.route(`${url}`).as(alias)
},
))
it('stubbed flagged as expected', testFlagXhr(
undefined,
[{
command: 'route',
alias,
type: 'stub',
}],
() => {
cy.server()
cy.route(url, 'stubbed response').as(alias)
},
))
})
})
})
context('Cypress.ProxyLogging', () => {
describe('.logInterception', () => {
it('creates a fake log for unmatched requests', () => {
const interception = {
id: 'request123',
request: {
url: 'http://foo',
method: 'GET',
headers: {},
},
}
const route = {}
const ret = Cypress.ProxyLogging.logInterception(interception, route)
expect(ret.preRequest).to.deep.eq({
requestId: 'request123',
resourceType: 'other',
originalResourceType: 'Request with no browser pre-request',
url: 'http://foo',
method: 'GET',
headers: {},
})
expect(ret.log.get('name')).to.eq('request')
})
})
})
})

View File

@@ -1,167 +0,0 @@
const { clickCommandLog } = require('../../support/utils')
const { _ } = Cypress
// https://github.com/cypress-io/cypress/pull/5299/files
describe('rect highlight', () => {
beforeEach(() => {
cy.visit('/fixtures/dom.html')
})
it('highlight elements are properly positioned', () => {
cy.$$('button:first').css({ boxSizing: 'content-box' })
getAndPin('button:first')
ensureCorrectHighlightPositions('button:first')
})
it('highlight elements properly positioned for content-box', () => {
getAndPin('button.content-box:first')
ensureCorrectHighlightPositions()
})
it('highlight inline elements', () => {
cy.$$('<span>foo</span>').prependTo(cy.$$('body'))
getAndPin('span:first')
ensureCorrectHighlightPositions('span:first')
})
it('highlight elements with css transform', () => {
cy.$$('button:first').css({ transform: 'scale(1.5) rotate(45deg)' })
getAndPin('button:first')
ensureCorrectHighlightPositions('button:first')
})
it('highlight elements with css transform on parent', () => {
cy.$$('<div id="parent">parent<div id="child">child</div></div>').css({
transform: 'scale(1.5) rotate(45deg) translate(100px, 20px)',
height: 40,
width: 60,
})
.prependTo(cy.$$('body'))
getAndPin('#child')
// TODO: assert covers element bounding-box
ensureCorrectHighlightPositions(null)
})
it('correct target position during click', () => {
clickAndPin('#button')
ensureCorrectHighlightPositions('#button')
ensureCorrectTargetPosition('#button')
})
it('correct target position during click with offset coords', () => {
clickAndPin('#button', 5, 10)
ensureCorrectHighlightPositions('#button')
ensureCorrectTargetPosition('#button')
})
// https://github.com/cypress-io/cypress/issues/7762
it('highlights above z-index elements', () => {
cy.$$('<div id="absolute-el"></div>').css({
position: 'absolute',
zIndex: 1000,
top: 0,
left: 0,
height: 50,
width: 50,
padding: 20,
margin: 20,
backgroundColor: 'salmon',
}).appendTo(cy.$$('body'))
getAndPin('#absolute-el')
ensureCorrectHighlightPositions('#absolute-el')
})
})
const ensureCorrectTargetPosition = (sel) => {
return cy.wrap(null, { timeout: 400 }).should(() => {
const target = cy.$$('div[data-highlight-hitbox]')[0].getBoundingClientRect()
const dims = {
left: target.left + target.width / 2,
right: target.left + target.width / 2,
top: target.top + target.height / 2,
bottom: target.top + target.height / 2,
width: 1,
height: 1,
}
expectToBeInside(dims, cy.$$(sel)[0].getBoundingClientRect(), 'border-box to match selector bounding-box')
})
}
const ensureCorrectHighlightPositions = (sel) => {
return cy.wrap(null, { timeout: 400 }).should(() => {
const els = {
content: cy.$$('div[data-layer=Content]'),
padding: cy.$$('div[data-layer=Padding]'),
border: cy.$$('div[data-layer=Border]'),
}
const dims = _.mapValues(els, ($el) => {
return $el[0].getBoundingClientRect()
})
const doc = els.content[0].ownerDocument
const contentHighlightCenter = [dims.content.x + dims.content.width / 2, dims.content.y + dims.content.height / 2]
expect(doc.elementFromPoint(...contentHighlightCenter)).eq(els.content[0])
expectToBeInside(dims.content, dims.padding, 'content to be inside padding')
expectToBeInside(dims.padding, dims.border, 'padding to be inside border')
if (sel) {
// assert convering bounding-box of element
expectToBeEqual(dims.border, cy.$$(sel)[0].getBoundingClientRect(), 'border-box to match selector bounding-box')
}
})
}
const getAndPin = (sel) => {
cy.get(sel)
clickCommandLog(sel, 'message-text')
}
const clickAndPin = (sel, ...args) => {
cy.get(sel).click(...args)
clickCommandLog('click')
}
const expectToBeEqual = (rect1, rect2, mes = 'rect to be equal to rect') => {
try {
expect(rect1.width, 'width').to.be.closeTo(rect2.width, 1)
expect(rect1.height, 'height').to.be.closeTo(rect2.height, 1)
expect(rect1.top, 'top').to.be.closeTo(rect2.top, 1)
expect(rect1.left, 'left').to.be.closeTo(rect2.left, 1)
expect(rect1.right, 'right').to.be.closeTo(rect2.right, 1)
expect(rect1.bottom, 'bottom').to.be.closeTo(rect2.bottom, 1)
} catch (e) {
e.message = `\nExpected ${mes}\n${e.message}`
throw e
}
}
const expectToBeInside = (rectInner, rectOuter, mes = 'rect to be inside rect') => {
try {
expect(rectInner.width, 'width').lte(rectOuter.width)
expect(rectInner.height, 'height').lte(rectOuter.height)
expect(rectInner.top, 'top').gte(rectOuter.top)
expect(rectInner.left, 'left').gte(rectOuter.left)
expect(rectInner.right, 'right').lte(rectOuter.right)
expect(rectInner.bottom, 'bottom').lte(rectOuter.bottom)
} catch (e) {
e.message = `\nExpected ${mes}\n${e.message}`
throw e
}
}

View File

@@ -1,39 +0,0 @@
describe('selector_playground', () => {
it('draws rect over currently hovered element', () => {
cy.visit('/fixtures/dom.html')
.then(() => {
// We trick the selector-playground into rendering while the test is running
top.Runner.configureMobx({ enforceActions: 'never' })
top.Runner.state.isRunning = false
const $highlightBtn = cy.$$('button.highlight-toggle:visible', top.document)
if (!$highlightBtn.length) {
const $btn = cy.$$('button.selector-playground-toggle', top.document)
$btn.click()
} else {
if (!$highlightBtn.hasClass('active')) {
$highlightBtn.click()
}
}
cy.get('input:first')
.trigger('mousemove', { force: true })
.should(expectToBeCovered)
})
})
})
/**
*
* @param {JQuery<HTMLElement>} $el
*/
const expectToBeCovered = ($el) => {
const el = $el[0]
const rect = el.getBoundingClientRect()
const elAtPoint = el.ownerDocument.elementFromPoint(rect.left, rect.top)
expect(el).not.eq(elAtPoint)
}

View File

@@ -1,415 +0,0 @@
/* eslint-disable @cypress/dev/skip-comment,mocha/no-exclusive-tests */
describe('per-test config', () => {
const testState = {
ranFirefox: false,
ranChrome: false,
ranChromium: false,
ranElectron: false,
}
after(function () {
if (hasOnly(this.currentTest)) return
if (Cypress.browser.family === 'firefox') {
return expect(testState).deep.eq({
ranChrome: false,
ranChromium: false,
ranFirefox: true,
ranElectron: false,
})
}
if (Cypress.browser.name === 'chrome') {
return expect(testState).deep.eq({
ranChrome: true,
ranChromium: false,
ranFirefox: false,
ranElectron: false,
})
}
if (Cypress.browser.name === 'chromium') {
return expect(testState).deep.eq({
ranChrome: false,
ranChromium: true,
ranFirefox: false,
ranElectron: false,
})
}
if (Cypress.browser.name === 'electron') {
return expect(testState).deep.eq({
ranChrome: false,
ranChromium: false,
ranFirefox: false,
ranElectron: true,
})
}
throw new Error('should have made assertion')
})
it('set various config values', {
defaultCommandTimeout: 200,
env: {
FOO_VALUE: 'foo',
},
}, () => {
cy.visit('/fixtures/generic.html')
expect(Cypress.config().defaultCommandTimeout).eq(200)
expect(Cypress.config('defaultCommandTimeout')).eq(200)
expect(Cypress.env('FOO_VALUE')).eq('foo')
})
it('does not leak various config values', {
}, () => {
expect(Cypress.config().defaultCommandTimeout).not.eq(200)
expect(Cypress.config('defaultCommandTimeout')).not.eq(200)
expect(Cypress.env('FOO_VALUE')).not.eq('foo')
})
it('can set viewport', {
viewportWidth: 400,
viewportHeight: 200,
}, () => {
expect(Cypress.config().viewportHeight).eq(200)
expect(Cypress.config().viewportWidth).eq(400)
})
it('can specify only run in chrome', {
browser: 'chrome',
}, () => {
testState.ranChrome = true
expect(Cypress.browser.name).eq('chrome')
})
it('can specify only run in chromium', {
browser: 'chromium',
}, () => {
testState.ranChromium = true
expect(Cypress.browser.family).eq('chromium')
})
it('can specify only run in firefox', {
browser: 'firefox',
}, () => {
testState.ranFirefox = true
expect(Cypress.browser.name).eq('firefox')
})
it('can specify only run in electron', {
browser: 'electron',
}, () => {
testState.ranElectron = true
expect(Cypress.browser.name).eq('electron')
})
describe('mutating global config via Cypress.config and Cypress.env', () => {
it('1/2 global config and env', {
defaultCommandTimeout: 1234,
env: {
FOO: '0',
},
}, () => {
Cypress.config('responseTimeout', 1111)
expect(Cypress.config('responseTimeout')).eq(1111)
expect(Cypress.config('defaultCommandTimeout')).eq(1234)
Cypress.env('BAR', '1')
expect(Cypress.env('FOO')).eq('0')
expect(Cypress.env('BAR')).eq('1')
})
it('2/2 global config and env', () => {
expect(Cypress.config('responseTimeout')).eq(1111)
expect(Cypress.config('defaultCommandTimeout')).eq(4000)
expect(Cypress.env('FOO')).eq(undefined)
expect(Cypress.env('BAR')).eq('1')
})
})
describe('in beforeEach', () => {
it('set various config values', {
defaultCommandTimeout: 200,
env: {
FOO_VALUE: 'foo',
},
}, () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
expect(Cypress.config('defaultCommandTimeout')).eq(200)
expect(Cypress.env('FOO_VALUE')).eq('foo')
})
beforeEach(() => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
expect(Cypress.config('defaultCommandTimeout')).eq(200)
expect(Cypress.env('FOO_VALUE')).eq('foo')
})
})
describe('in afterEach', () => {
it('set various config values', {
defaultCommandTimeout: 200,
env: {
FOO_VALUE: 'foo',
},
}, () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
expect(Cypress.config('defaultCommandTimeout')).eq(200)
expect(Cypress.env('FOO_VALUE')).eq('foo')
})
afterEach(() => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
expect(Cypress.config('defaultCommandTimeout')).eq(200)
expect(Cypress.env('FOO_VALUE')).eq('foo')
})
})
describe('in suite', () => {
describe('config in suite', {
defaultCommandTimeout: 200,
}, () => {
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
})
})
describe('in suite (context)', () => {
context('config in suite', {
defaultCommandTimeout: 200,
}, () => {
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
it('test', () => {
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
})
})
describe('in nested suite', () => {
describe('config in suite', {
foo: true,
defaultCommandTimeout: 200,
}, () => {
it('has config.foo', () => {
expect(Cypress.config().foo).ok
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
describe('inner suite', {
bar: true,
}, () => {
it('has config.bar', () => {
expect(Cypress.config().bar).ok
})
it('has config.bar and config.foo', () => {
expect(Cypress.config().bar).ok
expect(Cypress.config().foo).ok
expect(Cypress.config().defaultCommandTimeout).eq(200)
})
})
})
})
describe('in double nested suite', () => {
describe('config in suite', {
foo: true,
}, () => {
describe('inner suite', { bar: true }, () => {
it('has config.bar', () => {
expect(Cypress.config().bar).ok
expect(Cypress.config().foo).ok
})
})
})
})
describe('in mulitple nested suites', {
foo: false,
}, () => {
describe('config in suite', {
foo: true,
}, () => {
describe('inner suite 1', { bar: true }, () => {
it('has config.bar', () => {
expect(Cypress.config().bar).ok
expect(Cypress.config().foo).ok
})
})
describe('inner suite 2', { baz: true }, () => {
it('has config.baz', () => {
expect(Cypress.config().bar).not.ok
expect(Cypress.config().baz).ok
expect(Cypress.config().foo).ok
})
})
describe('inner suite 3', () => {
it('has only config.foo', () => {
expect(Cypress.config().bar).not.ok
expect(Cypress.config().baz).not.ok
expect(Cypress.config().foo).ok
})
})
})
})
describe('in multiple nested suites', () => {
describe('config in suite', {
foo: true,
}, () => {
describe('inner suite 1', { bar: true }, () => {
it('has config.bar', () => {
expect(Cypress.config().bar).ok
expect(Cypress.config().foo).ok
})
})
describe('inner suite 2', { baz: true }, () => {
it('has config.bar', () => {
expect(Cypress.config().bar).not.ok
expect(Cypress.config().baz).ok
expect(Cypress.config().foo).ok
})
})
})
})
describe('empty config', {}, () => {
it('empty config in test', {}, () => {
expect(true).ok
})
})
specify('works with it & specify', { defaultCommandTimeout: 100 }, () => {
expect(Cypress.config().defaultCommandTimeout).eq(100)
})
describe('config changes after run', () => {
let defaultCommandTimeout
it('1/2', {
defaultCommandTimeout: 1234,
}, () => {
cy.on('test:after:run', () => {
defaultCommandTimeout = Cypress.config('defaultCommandTimeout')
})
})
it('2/2', () => {
expect(defaultCommandTimeout).eq(1234)
})
})
describe('xit, xdescribe', () => {
xit('should be skipped', {}, () => {})
xspecify('should be skipped', {}, () => {})
xdescribe('suite should be skipped', {}, () => {
it('skipped')
})
xcontext('suite should be skipped', {}, () => {
it('skipped')
})
describe('non-skipped test', () => {
it('foo', () => {})
})
after(function () {
expect(this.currentTest?.parent?.parent?.tests).length(2)
expect(this.currentTest?.parent?.parent?.tests[0]).property('state', 'pending')
expect(this.currentTest?.parent?.parent?.tests[1]).property('state', 'pending')
expect(this.currentTest?.parent?.parent?.suites[0].tests[0]).property('state', 'pending')
expect(this.currentTest?.parent?.parent?.suites[1].tests[0]).property('state', 'pending')
})
})
})
describe('viewport', () => {
// https://github.com/cypress-io/cypress/issues/7631
it('can set viewport in testConfigOverrides', { viewportWidth: 200, viewportHeight: 100 }, () => {
cy.visit('/fixtures/generic.html')
cy.window().then((win) => {
expect(win.innerWidth).eq(200)
expect(win.innerHeight).eq(100)
})
})
it('viewport does not leak between tests', () => {
cy.visit('/fixtures/generic.html')
cy.window().then((win) => {
expect(win.innerWidth).eq(1000)
expect(win.innerHeight).eq(660)
})
})
})
describe('testConfigOverrides baseUrl @slow', () => {
it('visit 1', { baseUrl: 'http://localhost:3501' }, () => {
cy.visit('/fixtures/generic.html')
cy.url().should('eq', 'http://localhost:3501/fixtures/generic.html')
})
it('visit 2', { baseUrl: 'http://localhost:3500' }, () => {
cy.visit('/fixtures/generic.html')
cy.url().should('eq', 'http://localhost:3500/fixtures/generic.html')
})
})
describe('cannot set read-only properties', () => {
afterEach(() => {
window.top.__cySkipValidateConfig = true
})
it('throws if mutating read-only config with Cypress.config()', (done) => {
window.top.__cySkipValidateConfig = false
cy.on('fail', (err) => {
expect(err.message).to.include('`Cypress.config()` cannot mutate option `chromeWebSecurity` because it is a read-only property.')
done()
})
Cypress.config('chromeWebSecurity', false)
})
it('does not throw for non-Cypress config values', () => {
expect(() => {
Cypress.config('foo', 'bar')
}).to.not.throw()
})
})
function hasOnly (test) {
let curSuite = test.parent
let hasOnly = false
while (curSuite) {
if (curSuite._onlySuites.length || curSuite._onlyTests.length) {
hasOnly = true
}
curSuite = curSuite.parent
}
return hasOnly
}