@revoloo/cypress6
Version:
Cypress.io end to end testing tool
1,686 lines (1,426 loc) • 136 kB
JavaScript
('../spec_helper')
const _ = require('lodash')
let r = require('@cypress/request')
const rp = require('@cypress/request-promise')
const compression = require('compression')
const dns = require('dns')
const express = require('express')
const http = require('http')
const path = require('path')
const url = require('url')
let zlib = require('zlib')
const str = require('underscore.string')
const evilDns = require('evil-dns')
const Promise = require('bluebird')
const httpsServer = require(`${root}../https-proxy/test/helpers/https_server`)
const pkg = require('@packages/root')
const SseStream = require('ssestream')
const EventSource = require('eventsource')
const config = require(`${root}lib/config`)
const { ServerE2E } = require(`${root}lib/server-e2e`)
const { ProjectE2E } = require(`${root}lib/project-e2e`)
const Watchers = require(`${root}lib/watchers`)
const pluginsModule = require(`${root}lib/plugins`)
const preprocessor = require(`${root}lib/plugins/preprocessor`)
const resolve = require(`${root}lib/util/resolve`)
const { fs } = require(`${root}lib/util/fs`)
const glob = require(`${root}lib/util/glob`)
const CacheBuster = require(`${root}lib/util/cache_buster`)
const Fixtures = require(`${root}test/support/helpers/fixtures`)
zlib = Promise.promisifyAll(zlib)
// force supertest-session to use promises provided in supertest
const session = proxyquire('supertest-session', { supertest })
const absolutePathRegex = /"\/[^{}]*?\.projects/g
let sourceMapRegex = /\n\/\/# sourceMappingURL\=.*/
const replaceAbsolutePaths = (content) => {
return content.replace(absolutePathRegex, '"/<path-to-project>')
}
const removeWhitespace = function (c) {
c = str.clean(c)
c = str.lines(c).join(' ')
return c
}
const cleanResponseBody = (body) => {
return replaceAbsolutePaths(removeWhitespace(body))
}
describe('Routes', () => {
require('mocha-banner').register()
beforeEach(function () {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
sinon.stub(CacheBuster, 'get').returns('-123')
sinon.stub(ServerE2E.prototype, 'reset')
sinon.stub(pluginsModule, 'has').returns(false)
nock.enableNetConnect()
this.setup = (initialUrl, obj = {}) => {
if (_.isObject(initialUrl)) {
obj = initialUrl
initialUrl = null
}
if (!obj.projectRoot) {
obj.projectRoot = '/foo/bar/'
}
// get all the config defaults
// and allow us to override them
// for each test
return config.set(obj)
.then((cfg) => {
// use a jar for each test
// but reset it automatically
// between test
const jar = rp.jar()
this.r = function (options = {}, cb) {
_.defaults(options, {
proxy: this.proxy,
jar,
simple: false,
followRedirect: false,
resolveWithFullResponse: true,
})
return r(options, cb)
}
// use a custom request promise
// to automatically backfill these
// options including our proxy
this.rp = (options = {}) => {
let targetUrl
if (_.isString(options)) {
targetUrl = options
options = {}
}
_.defaults(options, {
url: targetUrl,
proxy: this.proxy,
jar,
simple: false,
followRedirect: false,
resolveWithFullResponse: true,
})
return rp(options)
}
const open = () => {
this.project = new ProjectE2E('/path/to/project-e2e')
cfg.pluginsFile = false
return Promise.all([
// open our https server
httpsServer.start(8443),
// and open our cypress server
(this.server = new ServerE2E(new Watchers())),
this.server.open(cfg, this.project)
.spread(async (port) => {
const automationStub = {
use: () => { },
}
await this.server.startWebsockets(automationStub, config, {})
if (initialUrl) {
this.server._onDomainSet(initialUrl)
}
this.srv = this.server.getHttpServer()
this.session = session(this.srv)
this.proxy = `http://localhost:${port}`
}),
pluginsModule.init(cfg, {
projectRoot: cfg.projectRoot,
}),
])
}
if (this.server) {
return Promise.join(
httpsServer.stop(),
this.server.close(),
)
.then(open)
}
return open()
})
}
})
afterEach(function () {
evilDns.clear()
nock.cleanAll()
Fixtures.remove()
this.session.destroy()
preprocessor.close()
this.project = null
return Promise.join(
this.server.close(),
httpsServer.stop(),
)
})
context('GET /', () => {
beforeEach(function () {
return this.setup()
})
// this tests a situation where we open our browser in another browser
// without proxy mode set
it('redirects to config.clientRoute without a _remoteOrigin and without a proxy', function () {
return this.rp({
url: this.proxy,
proxy: null,
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers.location).to.eq('/__/')
})
})
it('does not redirect with _remoteOrigin set', function () {
return this.setup('http://www.github.com')
.then(() => {
nock(this.server._remoteOrigin)
.get('/')
.reply(200, '<html></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.github.com/',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<html>')
expect(res.body).to.include('document.domain = \'github.com\'')
expect(res.body).to.include('</html>')
})
})
})
it('does not redirect when visiting http site which isnt cypress server', function () {
// this tests the 'default' state of cypress when a server
// is instantiated. i noticed that before you do your first
// cy.visit() that all http sites would redirect which is
// incorrect. we only want the cypress port to redirect initially
nock('http://www.momentjs.com')
.get('/')
.reply(200)
return this.rp('http://www.momentjs.com/')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers.location).not.to.eq('/__/')
})
})
it('proxies through https', function () {
return this.setup('https://localhost:8443')
.then(() => {
return this.rp({
url: 'https://localhost:8443/',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).not.to.include('Cypress')
expect(res.body).to.include('document.domain = \'localhost\'')
expect(res.body).to.include('<body>https server</body>')
})
})
})
})
context('GET /__', () => {
beforeEach(function () {
return this.setup({ projectName: 'foobarbaz' })
})
it('routes config.clientRoute to serve cypress client app html', function () {
return this.rp('http://localhost:2020/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/Runner.start/)
})
})
it('sets title to projectName', function () {
return this.rp('http://localhost:2020/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<title>foobarbaz</title>')
})
})
it('omits x-powered-by', function () {
return this.rp('http://localhost:2020/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['x-powered-by']).not.to.exist
})
})
it('proxies through https', function () {
return this.setup('https://localhost:8443')
.then(() => {
return this.rp('https://localhost:8443/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/Runner.start\(.+\)/)
})
})
})
it('clientRoute routes to \'not launched through Cypress\' without a proxy set', function () {
return this.rp({
url: `${this.proxy}/__`,
proxy: null,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/This browser was not launched through Cypress\./)
})
})
it('other URLs redirect to clientRoute without a proxy set', function () {
// test something that isn't the clientRoute
return this.rp({
url: `${this.proxy}/__cypress/xhrs/foo`,
proxy: null,
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.eq('/__/')
})
})
it('routes when baseUrl is set', function () {
return this.setup({ baseUrl: 'http://localhost:9999/app' })
.then(() => {
return this.rp('http://localhost:9999/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/Runner.start/)
})
})
})
it('sends Cypress.version', function () {
return this.setup({ baseUrl: 'http://localhost:9999/app' })
.then(() => {
return this.rp('http://localhost:9999/__')
.then((res) => {
expect(res.statusCode).to.eq(200)
const base64Config = /Runner\.start\(.*, "(.*)"\)/.exec(res.body)[1]
const configStr = Buffer.from(base64Config, 'base64').toString()
expect(configStr).to.include('version')
expect(configStr).to.include(pkg.version)
})
})
})
})
context('GET /__cypress/runner/*', () => {
beforeEach(function () {
return this.setup('http://localhost:8443')
})
it('can get cypress_runner.js', function () {
return this.rp('http://localhost:8443/__cypress/runner/cypress_runner.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/React/)
})
})
it('can get cypress_runner.css', function () {
return this.rp('http://localhost:8443/__cypress/runner/cypress_runner.css')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(/spec-iframe/)
})
})
})
context('GET /__cypress/files', () => {
beforeEach(() => {
Fixtures.scaffold('todos')
return Fixtures.scaffold('ids')
})
describe('todos with specific configuration', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('todos'),
config: {
integrationFolder: 'tests',
fixturesFolder: 'tests/_fixtures',
supportFile: 'tests/_support/spec_helper.js',
javascripts: ['tests/etc/**/*'],
},
})
})
it('returns base json file path objects of only tests', function () {
// this should omit any _fixture files, _support files and javascripts
return glob(path.join(Fixtures.projectPath('todos'), 'tests', '_fixtures', '**', '*'))
.then((files) => {
// make sure there are fixtures in here!
expect(files.length).to.be.gt(0)
return glob(path.join(Fixtures.projectPath('todos'), 'tests', '_support', '**', '*'))
.then((files) => {
// make sure there are support files in here!
expect(files.length).to.be.gt(0)
return this.rp({
url: 'http://localhost:2020/__cypress/files',
json: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const {
body,
} = res
expect(body.integration).to.have.length(4)
// remove the absolute path key
body.integration = _.map(body.integration, (obj) => {
return _.pick(obj, 'name', 'relative')
})
expect(res.body).to.deep.eq({
integration: [
{
'name': 'sub/a&b%c.js',
'relative': 'tests/sub/a&b%c.js',
},
{
name: 'sub/sub_test.coffee',
relative: 'tests/sub/sub_test.coffee',
},
{
name: 'test1.js',
relative: 'tests/test1.js',
},
{
name: 'test2.coffee',
relative: 'tests/test2.coffee',
},
],
})
})
})
})
})
})
describe('ids with regular configuration', () => {
it('returns test files as json ignoring *.hot-update.js', function () {
return this.setup({
projectRoot: Fixtures.projectPath('ids'),
})
.then(() => {
return this.rp({
url: 'http://localhost:2020/__cypress/files',
json: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const {
body,
} = res
expect(body.integration).to.have.length(6)
// remove the absolute path key
body.integration = _.map(body.integration, (obj) => {
return _.pick(obj, 'name', 'relative')
})
expect(body).to.deep.eq({
integration: [
{
name: 'bar.js',
relative: 'cypress/integration/bar.js',
},
{
name: 'baz.js',
relative: 'cypress/integration/baz.js',
},
{
name: 'dom.jsx',
relative: 'cypress/integration/dom.jsx',
},
{
name: 'foo.coffee',
relative: 'cypress/integration/foo.coffee',
},
{
name: 'nested/tmp.js',
relative: 'cypress/integration/nested/tmp.js',
},
{
name: 'noop.coffee',
relative: 'cypress/integration/noop.coffee',
},
],
})
})
})
})
it('can ignore other files as well', function () {
return this.setup({
projectRoot: Fixtures.projectPath('ids'),
config: {
ignoreTestFiles: ['**/bar.js', 'foo.coffee', '**/*.hot-update.js', '**/nested/*'],
},
})
.then(() => {
return this.rp({
url: 'http://localhost:2020/__cypress/files',
json: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const {
body,
} = res
expect(body.integration).to.have.length(3)
// remove the absolute path key
body.integration = _.map(body.integration, (obj) => {
return _.pick(obj, 'name', 'relative')
})
expect(body).to.deep.eq({
integration: [
{
name: 'baz.js',
relative: 'cypress/integration/baz.js',
},
{
name: 'dom.jsx',
relative: 'cypress/integration/dom.jsx',
},
{
name: 'noop.coffee',
relative: 'cypress/integration/noop.coffee',
},
],
})
})
})
})
})
})
context('GET /__cypress/tests', () => {
describe('ids with typescript', () => {
beforeEach(function () {
Fixtures.scaffold('ids')
return this.setup({
projectRoot: Fixtures.projectPath('ids'),
})
})
it('processes foo.coffee spec', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/foo.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('expect("foo.coffee")')
})
})
it('processes dom.jsx spec', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/baz.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('React.createElement(')
})
})
it('serves error javascript file when the file is missing', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=does/not/exist.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('Cypress.action("spec:script:error", {')
expect(res.body).to.include('Module not found')
})
})
})
describe('ids without typescript', () => {
beforeEach(function () {
Fixtures.scaffold('ids')
sinon.stub(resolve, 'typescript').callsFake(() => {
return null
})
return this.setup({
projectRoot: Fixtures.projectPath('ids'),
})
})
it('processes foo.coffee spec', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/foo.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(sourceMapRegex)
expect(res.body).to.include('expect("foo.coffee")')
})
})
it('processes dom.jsx spec', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/baz.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(sourceMapRegex)
expect(res.body).to.include('React.createElement(')
})
})
it('serves error javascript file when the file is missing', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=does/not/exist.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('Cypress.action("spec:script:error", {')
expect(res.body).to.include('Module not found')
})
})
})
describe('failures', () => {
beforeEach(function () {
Fixtures.scaffold('failures')
return this.setup({
projectRoot: Fixtures.projectPath('failures'),
})
})
it('serves error javascript file when there\'s a syntax error', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/syntax_error.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('Cypress.action("spec:script:error", {')
expect(res.body).to.include('Unexpected token')
})
})
})
describe('no-server', () => {
beforeEach(function () {
Fixtures.scaffold('no-server')
return this.setup({
projectRoot: Fixtures.projectPath('no-server'),
config: {
integrationFolder: 'my-tests',
javascripts: ['helpers/includes.js'],
},
})
})
it('processes my-tests/test1.js spec', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=my-tests/test1.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(sourceMapRegex)
expect(res.body).to.include(`expect('no-server')`)
})
})
it('processes helpers/includes.js javascripts', function () {
return this.rp('http://localhost:2020/__cypress/tests?p=helpers/includes.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.match(sourceMapRegex)
expect(res.body).to.include(`console.log('includes')`)
})
})
})
})
context('ALL /__cypress/xhrs/*', () => {
beforeEach(function () {
return this.setup()
})
describe('delay', () => {
it('can set delay to 10ms', function () {
const delay = sinon.spy(Promise, 'delay')
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-delay': '10',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(delay).to.be.calledWith(10)
})
})
it('does not call Promise.delay when no delay', function () {
const delay = sinon.spy(Promise, 'delay')
return this.rp('http://localhost:2020/__cypress/xhrs/users/1')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(delay).not.to.be.called
})
})
})
describe('status', () => {
it('can set status', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-status': '401',
},
})
.then((res) => {
expect(res.statusCode).to.eq(401)
})
})
})
describe('headers', () => {
it('can set headers', function () {
const headers = JSON.stringify({
'x-token': '123',
'content-type': 'text/plain',
})
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-headers': headers,
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/plain/)
expect(res.headers['x-token']).to.eq('123')
})
})
it('sets headers from response type', function () {
const headers = JSON.stringify({
'x-token': '123',
})
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
json: true,
headers: {
'x-cypress-headers': headers,
'x-cypress-response': JSON.stringify({ foo: 'bar' }),
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.headers['x-token']).to.eq('123')
expect(res.body).to.deep.eq({ foo: 'bar' })
})
})
})
describe('response', () => {
it('sets response to json', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
json: true,
headers: {
'x-cypress-response': JSON.stringify([1, 2, 3]),
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.body).to.deep.eq([1, 2, 3])
})
})
it('sets response to text/html', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-response': '<html>foo</html>',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/html/)
expect(res.body).to.eq('<html>foo</html>')
})
})
it('sets response to text/plain', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-response': 'foobarbaz',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/plain/)
expect(res.body).to.eq('foobarbaz')
})
})
it('sets response to text/plain on empty response', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
headers: {
'x-cypress-response': '',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/plain/)
expect(res.body).to.eq('')
})
})
it('decodes responses', function () {
const response = encodeURI(JSON.stringify({
'test': 'We’ll',
}))
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/users/1',
json: true,
headers: {
'x-cypress-response': response,
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.deep.eq({ test: 'We’ll' })
})
})
context('fixture', () => {
beforeEach(function () {
Fixtures.scaffold('todos')
return this.setup({
projectRoot: Fixtures.projectPath('todos'),
config: {
integrationFolder: 'tests',
fixturesFolder: 'tests/_fixtures',
},
})
})
it('returns fixture contents', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/bar',
json: true,
headers: {
'x-cypress-response': 'fixture:foo',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.body).to.deep.eq([{ json: true }])
})
})
it('returns __error on fixture errors', function () {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/bar',
json: true,
headers: {
'x-cypress-response': 'fixture:bad_json',
},
})
.then((res) => {
expect(res.statusCode).to.eq(400)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.body.__error).to.include('\'bad_json\' is not valid JSON.')
})
})
it('can change the fixture encoding', function () {
return fs.readFileAsync(Fixtures.projectPath('todos/tests/_fixtures/images/flower.png'), 'binary')
.then((bin) => {
return this.rp({
url: 'http://localhost:2020/__cypress/xhrs/bar',
headers: {
'x-cypress-response': 'fixture:images/flower.png,binary',
'x-cypress-headers': JSON.stringify({
'content-type': 'binary/octet-stream',
}),
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.include('binary/octet-stream')
expect(res.headers['content-length']).to.eq(`${bin.length}`)
})
})
})
})
context('PUT', () => {
it('can issue PUT requests', function () {
return this.rp({
method: 'put',
url: 'http://localhost:2020/__cypress/xhrs/users/1',
json: true,
body: {
name: 'brian',
},
headers: {
'x-cypress-response': JSON.stringify({ id: 123, name: 'brian' }),
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.body).to.deep.eq({ id: 123, name: 'brian' })
})
})
})
context('POST', () => {
it('can issue POST requests', function () {
return this.rp({
method: 'post',
url: 'http://localhost:2020/__cypress/xhrs/users/1',
json: true,
body: {
name: 'brian',
},
headers: {
'x-cypress-response': JSON.stringify({ id: 123, name: 'brian' }),
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/application\/json/)
expect(res.body).to.deep.eq({ id: 123, name: 'brian' })
})
})
})
context('HEAD', () => {
it('can issue PUT requests', function () {
return this.rp({
method: 'head',
url: 'http://localhost:2020/__cypress/xhrs/users/1',
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/plain/)
})
})
})
context('DELETE', () => {
it('can issue DELETE requests', function () {
return this.rp({
method: 'delete',
url: 'http://localhost:2020/__cypress/xhrs/users/1',
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/plain/)
})
})
})
})
})
// describe "maximum header size", ->
// ## https://github.com/cypress-io/cypress/issues/76
// it "does not bomb on huge headers", ->
// json = Fixtures.get("server/really_big_json.json")
// supertest(@srv)
// .get("/__cypress/xhrs/users")
// .set("x-cypress-response", json)
// .set("x-cypress-response-2", json)
// .expect(200)
// .expect("Content-Type", /application\/json/)
// .expect(JSON.parse(json))
context('GET /__cypress/iframes/*', () => {
beforeEach(() => {
Fixtures.scaffold('e2e')
Fixtures.scaffold('todos')
Fixtures.scaffold('no-server')
return Fixtures.scaffold('ids')
})
describe('todos', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('todos'),
config: {
integrationFolder: 'tests',
fixturesFolder: 'tests/_fixtures',
supportFile: 'tests/_support/spec_helper.js',
javascripts: ['tests/etc/etc.js'],
},
})
})
it('renders iframe with variables passed in', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_todos_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/integration/test2.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
it('can send back all tests', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_todos_all_tests_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/__all')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
it('can send back tests matching spec filter', function () {
// only returns tests with "sub_test" in their names
const contents = removeWhitespace(Fixtures.get('server/expected_todos_filtered_tests_iframe.html'))
this.project.spec = {
specFilter: 'sub_test',
}
return this.rp('http://localhost:2020/__cypress/iframes/__all')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
console.log(body)
expect(body).to.eq(contents)
})
})
})
describe('no-server', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('no-server'),
config: {
integrationFolder: 'my-tests',
supportFile: 'helpers/includes.js',
fileServerFolder: 'foo',
},
})
})
it('renders iframe with variables passed in', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_no_server_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/integration/test1.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
})
describe('no-server with supportFile: false', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('no-server'),
config: {
integrationFolder: 'my-tests',
supportFile: false,
},
})
})
it('renders iframe without support file', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_no_server_no_support_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/__all')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
})
describe('e2e', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('e2e'),
config: {
integrationFolder: 'cypress/integration',
supportFile: 'cypress/support/commands.js',
},
})
})
it('omits support directories', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_e2e_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/integration/app_spec.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
})
describe('ids', () => {
beforeEach(function () {
return this.setup({
projectRoot: Fixtures.projectPath('ids'),
})
})
it('renders iframe with variables passed in', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_ids_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/integration/foo.coffee')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
it('can send back all tests', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_ids_all_tests_iframe.html'))
return this.rp('http://localhost:2020/__cypress/iframes/__all')
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
})
})
context('GET *', () => {
context('basic request', () => {
beforeEach(function () {
return this.setup('http://www.github.com')
})
it('basic 200 html response', function () {
nock(this.server._remoteOrigin)
.get('/')
.reply(200, 'hello from bar!', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.github.com/',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('hello from bar!')
})
})
})
context('gzip', () => {
beforeEach(function () {
return this.setup('http://www.github.com')
})
it('unzips, injects, and then rezips initial content', function () {
nock(this.server._remoteOrigin)
.get('/gzip')
.matchHeader('accept-encoding', 'gzip')
.replyWithFile(200, Fixtures.path('server/gzip.html.gz'), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.github.com/gzip',
gzip: true,
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<html>')
expect(res.body).to.include('gzip')
expect(res.body).to.include('Cypress.')
expect(res.body).to.include('document.domain = \'github.com\'')
expect(res.body).to.include('</html>')
})
})
it('unzips, injects, and then rezips regular http content', function () {
nock(this.server._remoteOrigin)
.get('/gzip')
.matchHeader('accept-encoding', 'gzip')
.replyWithFile(200, Fixtures.path('server/gzip.html.gz'), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.github.com/gzip',
gzip: true,
headers: {
'Cookie': '__cypress.initial=false',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<html>')
expect(res.body).to.include('gzip')
expect(res.body).to.include('document.domain = \'github.com\'')
expect(res.body).to.include('</html>')
})
})
it('does not inject on regular gzip\'d content', function () {
nock(this.server._remoteOrigin)
.get('/gzip')
.matchHeader('accept-encoding', 'gzip')
.replyWithFile(200, Fixtures.path('server/gzip.html.gz'), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.github.com/gzip',
gzip: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<html>')
expect(res.body).to.include('gzip')
expect(res.body).not.to.include('document.domain = \'github.com\'')
expect(res.body).to.include('</html>')
})
})
// https://github.com/cypress-io/cypress/issues/1746
it('can ungzip utf-8 javascript and inject without corrupting it', function () {
let js = ''
const app = express()
app.use(compression({ chunkSize: 64, threshold: 1 }))
app.get('/', (req, res) => {
res.setHeader('content-type', 'application/javascript; charset=UTF-8')
res.setHeader('transfer-encoding', 'chunked')
const write = (chunk) => {
js += chunk
return res.write(chunk)
}
// note - this is unintentionally invalid JS, just try executing it anywhere
write('function ')
_.times(100, () => {
return write('😡😈'.repeat(10))
})
write(' () { }')
return res.end()
})
const server = http.createServer(app)
return Promise.fromCallback((cb) => {
return server.listen(12345, cb)
}).then(() => {
return this.rp({
url: 'http://localhost:12345',
gzip: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.deep.eq(js)
})
}).finally(() => {
return Promise.fromCallback((cb) => {
return server.close(cb)
})
})
})
})
context('accept-encoding', () => {
beforeEach(function () {
return this.setup('http://www.github.com')
})
it('strips unsupported deflate and br encoding', function () {
nock(this.server._remoteOrigin)
.get('/accept')
.matchHeader('accept-encoding', 'gzip')
.reply(200, '<html>accept</html>')
return this.rp({
url: 'http://www.github.com/accept',
gzip: true,
headers: {
'accept-encoding': 'gzip,deflate,br',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('<html>accept</html>')
})
})
it('removes accept-encoding when nothing is supported', function () {
nock(this.server._remoteOrigin, {
badheaders: ['accept-encoding'],
})
.get('/accept')
.reply(200, '<html>accept</html>')
return this.rp({
url: 'http://www.github.com/accept',
gzip: true,
headers: {
'accept-encoding': 'foo,bar,baz',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('<html>accept</html>')
})
})
})
context('304 Not Modified', () => {
beforeEach(function () {
return this.setup('http://localhost:8080')
})
it('sends back a 304', function () {
nock(this.server._remoteOrigin)
.get('/assets/app.js')
.reply(304)
return this.rp({
url: 'http://localhost:8080/assets/app.js',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(304)
})
})
})
context('redirects', () => {
beforeEach(function () {
return this.setup('http://getbootstrap.com')
})
it('passes the location header through', function () {
nock(this.server._remoteOrigin)
.get('/foo')
.reply(302, undefined, {
'Location': '/',
})
.get('/')
.reply(200, '<html></html', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://getbootstrap.com/foo',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/')
expect(res.headers['location']).to.eq('/')
})
})
// this fixes improper url merge where we took query params
// and added them needlessly
it('doesnt redirect with query params or hashes which werent in location header', function () {
nock(this.server._remoteOrigin)
.get('/foo?bar=baz')
.reply(302, undefined, {
'Location': '/css',
})
return this.rp({
url: 'http://getbootstrap.com/foo?bar=baz',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/')
expect(res.headers['location']).to.eq('/css')
})
})
it('does redirect with query params if location header includes them', function () {
nock(this.server._remoteOrigin)
.get('/foo?bar=baz')
.reply(302, undefined, {
'Location': '/css?q=search',
})
.get('/')
.reply(200, '<html></html', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://getbootstrap.com/foo?bar=baz',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/')
expect(res.headers['location']).to.eq('/css?q=search')
})
})
it('does redirect with query params to external domain if location header includes them', function () {
nock(this.server._remoteOrigin)
.get('/foo?bar=baz')
.reply(302, undefined, {
'Location': 'https://www.google.com/search?q=cypress',
})
.get('/')
.reply(200, '<html></html', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://getbootstrap.com/foo?bar=baz',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/')
expect(res.headers['location']).to.eq('https://www.google.com/search?q=cypress')
})
})
it('sets cookies and removes __cypress.initial when initial is originally false', function () {
nock(this.server._remoteOrigin)
.get('/css')
.reply(302, undefined, {
'Set-Cookie': 'foo=bar; Path=/',
'Location': '/css/',
})
return this.rp({
url: 'http://getbootstrap.com/css',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
// since this is not a cypress cookie we do not set the domain
expect(res.headers['set-cookie']).to.deep.eq(['foo=bar; Path=/'])
expect(res.headers['location']).to.eq('/css/')
})
})
return [301, 302, 303, 307, 308].forEach((code) => {
it(`handles direct for status code: ${code}`, function () {
return this.setup('http://auth.example.com')
.then(() => {
nock(this.server._remoteOrigin)
.get('/login')
.reply(code, undefined, {
Location: 'http://app.example.com/users/1',
})
return this.rp({
url: 'http://auth.example.com/login',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(code)
expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=example.com; Path=/')
})
})
})
})
})
context('error handling', () => {
beforeEach(function () {
return this.setup('http://www.github.com')
})
it('passes through status code + content', function () {
nock(this.server._remoteOrigin)
.get('/index.html')
.reply(500, 'server error', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.github.com/index.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(500)
expect(res.body).to.include('server error')
expect(res.body).to.include('document.domain = \'github.com\'')
expect(res.headers['set-cookie']).to.match(/__cypress.initial=;/)
})
})
it('sends back socket hang up on request errors which match origin', function () {
nock('http://app.github.com')
.get('/')
.replyWithError('ECONNREFUSED')
return this.rp({
url: 'http://app.github.com/',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
.then(() => {
throw new Error('should not reach')
}).catch((err) => {
expect(err.message).to.eq('Error: socket hang up')
})
})
it('sends back socket hang up on actual request errors', function () {
return this.setup('http://localhost:64644')
.then(() => {
return this.rp({
url: 'http://localhost:64644',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
}).then(() => {
throw new Error('should not reach')
}).catch((err) => {
expect(err.message).to.eq('Error: socket hang up')
})
})
it('sends back socket hang up on http errors when no matching origin', function () {
return this.rp({
url: 'http://localhost:64644',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
.then(() => {
throw new Error('should not reach')
}).catch((err) => {
expect(err.message).to.eq('Error: socket hang up')
})
})
it('sends back 401 when file server does not receive correct auth', function () {
return this.setup('<root>', {
config: {
fileServerFolder: '/Users/bmann/Dev/projects',
},
})
.then(() => {
return rp(`http://localhost:${this.server._fileServer.port()}/foo/views/test/index.html`, {
resolveWithFullResponse: true,
simple: false,
})
}).then((res) => {
expect(res.statusCode).to.eq(401)
})
})
it('sends back 404 when file does not exist locally', function () {
return this.setup('<root>', {
config: {
fileServerFolder: '/Users/bmann/Dev/projects',
},
})
.then(() => {