Files
cypress/packages/server/test/integration/http_requests_spec.js
T
Gleb Bahmutov fe96d7cf2a feat: run only filtered specs (#8007)
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
2020-07-30 10:29:21 -04:00

4115 lines
134 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
require('../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 browserify = require('browserify')
const babelify = require('babelify')
const coffeeify = require('coffeeify')
const streamToPromise = require('stream-to-promise')
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 Server = require(`${root}lib/server`)
const Project = require(`${root}lib/project`)
const Watchers = require(`${root}lib/watchers`)
const pluginsModule = require(`${root}lib/plugins`)
const preprocessor = require(`${root}lib/plugins/preprocessor`)
const resolve = require(`${root}lib/plugins/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`)
const simple_tsify = require(`${root}test/support/helpers/simple_tsify`)
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 removeSourceMap = (content) => {
return content.replace(sourceMapRegex, ';')
}
const removeWhitespace = function (c) {
c = str.clean(c)
c = str.lines(c).join(' ')
return c
}
const cleanResponseBody = (body) => {
return replaceAbsolutePaths(removeWhitespace(body))
}
const browserifyFile = (filePath) => {
return streamToPromise(
browserify({
entries: [filePath],
extensions: ['.js', '.jsx', '.coffee'],
cache: {},
packageCache: {},
transform: [
[coffeeify, {}],
[babelify, {
plugins: ['add-module-exports', '@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-transform-runtime'],
presets: ['@babel/preset-env', '@babel/preset-react'],
}],
],
})
.bundle(),
)
}
const browserifyFileTs = (filePath) => {
return streamToPromise(
browserify(filePath)
.transform(coffeeify)
.transform(simple_tsify, {
typescript: require('typescript'),
})
.bundle(),
)
}
describe('Routes', () => {
require('mocha-banner').register()
beforeEach(function () {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
sinon.stub(CacheBuster, 'get').returns('-123')
sinon.stub(Server.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 Project('/path/to/project')
return Promise.all([
// open our https server
httpsServer.start(8443),
// and open our cypress server
(this.server = new Server(new Watchers())),
this.server.open(cfg, this.project)
.spread((port) => {
if (initialUrl) {
this.server._onDomainSet(initialUrl)
}
this.srv = this.server.getHttpServer()
this.session = session(this.srv)
this.proxy = `http://localhost:${port}`
}),
])
}
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'),
})
})
const checkTranspilation = function (body, file) {
const b = removeSourceMap(body).replace(/\n/g, '')
const f = file.toString().replace(/\n/g, '')
expect(b).to.equal(f)
}
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)
return browserifyFileTs(Fixtures.path('projects/ids/cypress/integration/foo.coffee'))
.then((file) => {
return checkTranspilation(res.body, file)
})
})
})
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)
return browserifyFileTs(Fixtures.path('projects/ids/cypress/integration/baz.js'))
.then((file) => {
checkTranspilation(res.body, file)
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('Cannot find module')
})
})
})
describe('ids without typescript', () => {
beforeEach(function () {
Fixtures.scaffold('ids')
// remove cached options
delete require.cache[require.resolve('@cypress/browserify-preprocessor')]
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)
return browserifyFile(Fixtures.path('projects/ids/cypress/integration/foo.coffee'))
.then((file) => {
expect(removeSourceMap(res.body)).to.equal(file.toString())
})
})
})
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)
return browserifyFile(Fixtures.path('projects/ids/cypress/integration/baz.js'))
.then((file) => {
expect(removeSourceMap(res.body)).to.equal(file.toString())
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('Cannot find module')
})
})
})
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('ParseError')
})
})
})
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)
return browserifyFileTs(Fixtures.path('projects/no-server/my-tests/test1.js'))
.then((file) => {
expect(removeSourceMap(res.body)).to.equal(file.toString())
})
})
})
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)
return browserifyFileTs(Fixtures.path('projects/no-server/helpers/includes.js'))
.then((file) => {
expect(removeSourceMap(res.body)).to.equal(file.toString())
})
})
})
})
})
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': 'Well',
}))
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: 'Well' })
})
})
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(() => {
return this.rp(`${this.proxy}/foo/views/test/index.html`)
.then((res) => {
expect(res.statusCode).to.eq(404)
expect(res.body).to.include('Cypress errored trying to serve this file from your system:')
expect(res.body).to.include('/Users/bmann/Dev/projects/foo/views/test/index.html')
expect(res.body).to.include('The file was not found.')
expect(res.body).to.include('<html>\n<head> <script')
expect(res.body).to.include('</script> </head> <body>')
expect(res.body).to.include('document.domain = \'localhost\';')
})
})
}) // should continue to inject
it('does not inject on file server errors when origin does not match', function () {
return this.setup('<root>', {
config: {
fileServerFolder: '/Users/bmann/Dev/projects',
},
})
.then(() => {
nock('http://www.github.com')
.get('/index.html')
.reply(500, 'server error', {
'Content-Type': 'text/html',
})
return this.rp('http://www.github.com/index.html')
.then((res) => {
expect(res.statusCode).to.eq(500)
expect(res.body).to.eq('server error')
})
})
})
it('transparently proxies decoding gzip failures', function () {
nock(this.server._remoteOrigin)
.get('/index.html')
.replyWithFile(200, Fixtures.path('server/gzip-bad.html.gz'), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.github.com/index.html',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
gzip: true,
})
.then(() => {
throw new Error('should not reach')
}).catch((err) => {
expect(err.error.code).to.eq('ECONNRESET')
})
})
})
context('headers', () => {
beforeEach(function () {
return this.setup('http://localhost:8080')
})
describe('when unload is true', () => {
it('automatically redirects back to clientRoute', function () {
return this.rp({
url: 'http://localhost:8080/_',
headers: {
'Cookie': '__cypress.unload=true; __cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.eq('/__/')
})
})
})
describe('when initial is true', () => {
it('sets back to false', function () {
nock(this.server._remoteOrigin)
.get('/app.html')
.reply(200, 'OK', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/app.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['set-cookie']).to.match(/initial=;/)
})
})
})
describe('when initial is false', () => {
it('does not reset initial or remoteHost', function () {
nock(this.server._remoteOrigin)
.get('/app.html')
.reply(200, 'OK', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/app.html',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
// there shouldnt be any cookies set here by us
expect(res.headers['set-cookie']).not.to.exist
})
})
})
it('sends with Transfer-Encoding: chunked without Content-Length', function () {
nock(this.server._remoteOrigin)
.get('/login')
.reply(200, Buffer.from('foo'), {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/login',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('foo')
expect(res.headers['transfer-encoding']).to.eq('chunked')
expect(res.headers).not.to.have.property('content-length')
})
})
it('does not have Content-Length', function () {
nock(this.server._remoteOrigin)
.get('/login')
.reply(200, 'foo', {
'Content-Type': 'text/html',
'Content-Length': 123,
})
return this.rp({
url: 'http://localhost:8080/login',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('foo')
expect(res.headers['transfer-encoding']).to.eq('chunked')
expect(res.headers).not.to.have.property('content-length')
})
})
it('forwards cookies from incoming responses', function () {
nock(this.server._remoteOrigin)
.get('/login')
.reply(200, 'OK', {
'set-cookie': 'userId=123',
})
return this.rp({
url: 'http://localhost:8080/login',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['set-cookie']).to.match(/userId=123/)
})
})
it('appends to previous cookies from incoming responses', function () {
nock(this.server._remoteOrigin)
.get('/login')
.reply(200, '<html></html>', {
'set-cookie': 'userId=123; Path=/',
'content-type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/login',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const setCookie = res.headers['set-cookie']
expect(setCookie[0]).to.eq('userId=123; Path=/')
expect(setCookie[1]).to.eq('__cypress.initial=; Domain=localhost; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
})
})
it('appends cookies on redirects', function () {
nock(this.server._remoteOrigin)
.get('/login')
.reply(302, undefined, {
'location': '/dashboard',
'set-cookie': 'userId=123; Path=/',
})
return this.rp({
url: 'http://localhost:8080/login',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.eq('/dashboard')
expect(res.headers['set-cookie']).to.deep.eq([
'userId=123; Path=/',
'__cypress.initial=true; Domain=localhost; Path=/',
])
})
})
it('passes invalid cookies', function () {
nock(this.server._remoteOrigin)
.get('/invalid')
.reply(200, 'OK', {
'set-cookie': [
'foo=bar; Path=/',
'___utmvmXluIZsM=fidJKOsDSdm; path=/; Max-Age=900',
'___utmvbXluIZsM=bZM\n XtQOGalF: VtR; path=/; Max-Age=900',
],
})
return this.rp('http://localhost:8080/invalid')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['set-cookie']).to.deep.eq([
'foo=bar; Path=/',
'___utmvmXluIZsM=fidJKOsDSdm; path=/; Max-Age=900',
'___utmvbXluIZsM=bZM XtQOGalF: VtR; path=/; Max-Age=900',
])
})
})
it('forwards other headers from incoming responses', function () {
nock(this.server._remoteOrigin)
.get('/auth')
.reply(200, 'OK', {
'x-token': 'abc-123',
})
return this.rp({
url: 'http://localhost:8080/auth',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['x-token']).to.eq('abc-123')
})
})
it('forwards headers to outgoing requests', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.matchHeader('x-custom', 'value')
.reply(200, 'hello from bar!', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/bar',
headers: {
'Cookie': '__cypress.initial=true',
'x-custom': 'value',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('hello from bar!')
})
})
it('omits x-frame-options', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, 'OK', {
'Content-Type': 'text/html',
'x-frame-options': 'SAMEORIGIN',
})
return this.rp('http://localhost:8080/bar')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers).not.to.have.property('x-frame-options')
})
})
it('omits content-security-policy', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, 'OK', {
'Content-Type': 'text/html',
'content-security-policy': 'foobar;',
})
return this.rp({
url: 'http://localhost:8080/bar',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers).not.to.have.property('content-security-policy')
})
})
it('omits document-domain from Feature-Policy header', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, 'OK', {
'Content-Type': 'text/html',
'Feature-Policy': 'camera *; document-domain \'none\'; autoplay \'self\'',
})
return this.rp({
url: 'http://localhost:8080/bar',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['feature-policy']).to.include('camera *')
expect(res.headers['feature-policy']).to.include('autoplay \'self\'')
expect(res.headers['feature-policy']).not.to.include('document-domain \'none\'')
})
})
it('does not modify host origin header', function () {
return this.setup('http://foobar.com')
.then(() => {
nock(this.server._remoteOrigin)
.get('/css')
.matchHeader('host', 'foobar.com')
.reply(200)
return this.rp({
url: 'http://foobar.com/css',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
})
it('does not cache when initial response', function () {
nock(this.server._remoteOrigin)
.get('/')
.reply(200, 'hello from bar!', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['cache-control']).to.eq('no-cache, no-store, must-revalidate')
})
})
it('does cache requesting resource without injection', function () {
nock(this.server._remoteOrigin)
.get('/')
.reply(200, 'hello from bar!', {
'Content-Type': 'text/plain',
'Cache-Control': 'max-age=86400',
})
return this.rp('http://localhost:8080/')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['cache-control']).to.eq('max-age=86400')
})
})
it('forwards origin header', function () {
nock(this.server._remoteOrigin)
.get('/foo')
.matchHeader('host', 'localhost:8080')
.matchHeader('origin', 'http://localhost:8080')
.reply(200, '<html>origin</html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://localhost:8080/foo',
headers: {
'Cookie': '__cypress.initial=false',
'Origin': 'http://localhost:8080',
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('origin')
expect(res.body).to.include('document.domain = \'localhost\'')
expect(res.body).not.to.include('Cypress')
})
})
it('issue #222 - correctly sets http host headers', function () {
const matches = (url, fn) => {
return this.setup(url)
.then(() => {
this.server.onRequest(fn)
return this.rp(url)
}).then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('{}')
})
}
return matches('http://localhost:8080/app.css', () => {
return nock('http://localhost:8080')
.matchHeader('host', 'localhost:8080')
.get('/app.css')
.reply(200, '{}', {
'Content-Type': 'text/css',
})
}).then(() => {
return matches('http://127.0.0.1/app.css', () => {
return nock('http://127.0.0.1')
.matchHeader('host', '127.0.0.1')
.get('/app.css')
.reply(200, '{}', {
'Content-Type': 'text/css',
})
})
}).then(() => {
return matches('http://127.0.0.1:80/app2.css', () => {
return nock('http://127.0.0.1:80')
.matchHeader('host', '127.0.0.1')
.get('/app2.css')
.reply(200, '{}', {
'Content-Type': 'text/css',
})
})
}).then(() => {
return matches('https://www.google.com:443/app.css', () => {
return nock('https://www.google.com')
.matchHeader('host', 'www.google.com')
.get('/app.css')
.reply(200, '{}', {
'Content-Type': 'text/css',
})
})
}).then(() => {
return matches('https://www.apple.com/app.css', () => {
return nock('https://www.apple.com')
.matchHeader('host', 'www.apple.com')
.get('/app.css')
.reply(200, '{}', {
'Content-Type': 'text/css',
})
})
})
})
context('authorization', () => {
it('attaches auth headers when matches origin', function () {
const username = 'u'
const password = 'p'
const base64 = Buffer.from(`${username}:${password}`).toString('base64')
this.server._remoteAuth = {
username,
password,
}
nock('http://localhost:8080')
.get('/index')
.matchHeader('authorization', `Basic ${base64}`)
.reply(200, '')
return this.rp('http://localhost:8080/index')
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
it('does not attach auth headers when not matching origin', function () {
nock('http://localhost:8080', {
badheaders: ['authorization'],
})
.get('/index')
.reply(200, '')
return this.rp('http://localhost:8080/index')
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
it('does not modify existing auth headers when matching origin', function () {
const existing = 'Basic asdf'
this.server._remoteAuth = {
username: 'u',
password: 'p',
}
nock('http://localhost:8080')
.get('/index')
.matchHeader('authorization', existing)
.reply(200, '')
return this.rp({
url: 'http://localhost:8080/index',
headers: {
'Authorization': existing,
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
// https://github.com/cypress-io/cypress/issues/4267
it('doesn\'t attach auth headers to a diff protection space on the same origin', function () {
return this.setup('http://beta.something.com')
.then(() => {
const username = 'u'
const password = 'p'
const base64 = Buffer.from(`${username}:${password}`).toString('base64')
this.server._remoteAuth = {
username,
password,
}
nock(/.*\.something.com/)
.get('/index')
.matchHeader('authorization', `Basic ${base64}`)
.reply(200, '')
.get('/index')
.matchHeader('authorization', _.isUndefined)
.reply(200, '')
return this.rp('http://beta.something.com/index')
.then((res) => {
expect(res.statusCode).to.eq(200)
return this.rp('http://cdn.something.com/index')
}).then((res) => {
expect(res.statusCode).to.eq(200)
})
})
})
})
})
context('images', () => {
beforeEach(() => {
return Fixtures.scaffold()
})
it('passes the bytes through without injection on http servers', function () {
const image = Fixtures.projectPath('e2e/static/javascript-logo.png')
return Promise.all([
fs.statAsync(image).get('size'),
fs.readFileAsync(image, 'utf8'),
this.setup('http://localhost:8881'),
])
.spread((size, bytes, setup) => {
nock(this.server._remoteOrigin)
.get('/javascript-logo.png')
.replyWithFile(200, image, {
'Content-Type': 'image/png',
})
return this.rp({
url: 'http://localhost:8881/javascript-logo.png',
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).not.to.include('<script')
expect(res.body).to.eq(bytes)
})
})
})
it('passes the bytes through without injection on http servers with gzip', function () {
const image = Fixtures.projectPath('e2e/static/javascript-logo.png')
const zipped = Fixtures.projectPath('e2e/static/javascript-logo.png.gz')
return Promise.all([
fs.statAsync(image).get('size'),
fs.readFileAsync(image, 'utf8'),
this.setup('http://localhost:8881'),
])
.spread((size, bytes, setup) => {
nock(this.server._remoteOrigin)
.get('/javascript-logo.png')
.replyWithFile(200, zipped, {
'Content-Type': 'image/png',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://localhost:8881/javascript-logo.png',
gzip: true,
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).not.to.include('<script')
expect(res.body).to.eq(bytes)
})
})
})
})
context('woff', () => {
beforeEach(() => {
return Fixtures.scaffold()
})
it('passes the bytes through without injection', function () {
const font = Fixtures.projectPath('e2e/static/FiraSans-Regular.woff')
return Promise.all([
fs.statAsync(font).get('size'),
fs.readFileAsync(font, 'utf8'),
this.setup('http://localhost:8881'),
])
.spread((size, bytes, setup) => {
nock(this.server._remoteOrigin)
.get('/font.woff')
.replyWithFile(200, font, {
'Content-Type': 'application/font-woff; charset=utf-8',
})
return this.rp({
url: 'http://localhost:8881/font.woff',
headers: {
'Accept': '*/*',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).not.to.include('<script')
expect(res.body).to.eq(bytes)
})
})
})
})
context('svg', () => {
beforeEach(function () {
return this.setup('http://www.google.com')
})
it('rewrites <svg> without hanging', function () {
// if this test finishes without timing out we know its all good
const contents = removeWhitespace(Fixtures.get('server/err_response.html'))
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, contents, {
'Content-Type': 'text/html; charset=utf-8',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
})
context('content injection', () => {
beforeEach(function () {
return this.setup('http://www.google.com')
})
it('injects when head has attributes', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_head_inject.html'))
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html> <head prefix="og: foo"> <meta name="foo" content="bar"> </head> <body>hello from bar!</body> </html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
it('injects even when head tag is missing', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_no_head_tag_inject.html'))
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html> <body>hello from bar!</body> </html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
it('injects when head is capitalized', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<HTML> <HEAD>hello from bar!</HEAD> </HTML>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<HTML> <HEAD> <script type=\'text/javascript\'> document.domain = \'google.com\';')
})
})
it('injects when head missing but has <header>', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html> <body><nav>some nav</nav><header>header</header></body> </html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<html> <head> <script type=\'text/javascript\'> document.domain = \'google.com\';')
expect(res.body).to.include('</head> <body><nav>some nav</nav><header>header</header></body> </html>')
})
})
it('injects when body is capitalized', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<HTML> <BODY>hello from bar!</BODY> </HTML>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('</script> </head> <BODY>hello from bar!</BODY> </HTML>')
})
})
it('injects when both head + body are missing', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<HTML>hello from bar!</HTML>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<HTML> <head> <script')
expect(res.body).to.include('</head>hello from bar!</HTML>')
})
})
it('injects even when html + head + body are missing', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<div>hello from bar!</div>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<head> <script')
expect(res.body).to.include('</head><div>hello from bar!</div>')
})
})
it('injects after DOCTYPE declaration when no other content', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<!DOCTYPE>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('<!DOCTYPE><head> <script')
})
})
it('injects superdomain even when head tag is missing', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html> <body>hello from bar!</body> </html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
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.eq('<html> <head> <script type=\'text/javascript\'> document.domain = \'google.com\'; </script> </head> <body>hello from bar!</body> </html>')
})
})
it('injects content after following redirect', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(302, undefined, {
// redirect us to google.com!
'Location': 'http://www.google.com/foo',
})
nock(this.server._remoteOrigin)
.get('/foo')
.reply(200, '<html> <head prefix="og: foo"> <title>foo</title> </head> <body>hello from bar!</body> </html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.eq('http://www.google.com/foo')
expect(res.headers['set-cookie']).to.match(/initial=true/)
return this.rp(res.headers['location'])
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['set-cookie']).to.match(/initial=;/)
expect(res.body).to.include('Cypress.action(')
})
})
})
it('injects performantly on a huge amount of elements over http', function () {
Fixtures.scaffold()
nock(this.server._remoteOrigin)
.get('/elements.html')
.replyWithFile(200, Fixtures.projectPath('e2e/elements.html'), {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/elements.html',
headers: {
'Cookie': '__cypress.initial=true',
'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('document.domain = \'google.com\';')
})
})
it('injects performantly on a huge amount of elements over file', function () {
Fixtures.scaffold()
return this.setup('/index.html', {
projectRoot: Fixtures.projectPath('e2e'),
})
.then(() => {
return this.rp({
url: `${this.proxy}/elements.html`,
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('document.domain = \'localhost\';')
})
})
})
it('does not inject when not initial and not html', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html><head></head></html>', {
'Content-Type': 'text/plain',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('<html><head></head></html>')
})
})
it('injects into https server', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_https_inject.html'))
return this.setup('https://localhost:8443')
.then(() => {
return this.rp({
url: 'https://localhost:8443/',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents)
})
})
})
it('injects into https://www.google.com', function () {
return this.setup('https://www.google.com')
.then(() => {
this.server.onRequest((req, res) => {
return nock('https://www.google.com')
.get('/')
.reply(200, '<html><head></head><body>google</body></html>', {
'Content-Type': 'text/html',
})
})
return this.rp({
url: 'https://www.google.com/',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('Cypress.action(')
})
})
})
it('injects even on 5xx responses', function () {
return this.setup('https://www.google.com')
.then(() => {
this.server.onRequest((req, res) => {
return nock('https://www.google.com')
.get('/')
.reply(500, '<html><head></head><body>google</body></html>', {
'Content-Type': 'text/html',
})
})
return this.rp({
url: 'https://www.google.com/',
headers: {
'Accept': 'text/html, application/xhtml+xml, */*',
},
})
.then((res) => {
expect(res.statusCode).to.eq(500)
expect(res.body).to.include('document.domain = \'google.com\'')
})
})
})
it('works with host swapping', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_https_inject.html'))
return this.setup('https://www.foobar.com:8443')
.then(() => {
evilDns.add('*.foobar.com', '127.0.0.1')
return this.rp({
url: 'https://www.foobar.com:8443/index.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents.replace('localhost', 'foobar.com'))
})
})
})
it('continues to inject on the same https superdomain but different subdomain', function () {
const contents = removeWhitespace(Fixtures.get('server/expected_https_inject.html'))
return this.setup('https://www.foobar.com:8443')
.then(() => {
evilDns.add('*.foobar.com', '127.0.0.1')
return this.rp({
url: 'https://docs.foobar.com:8443/index.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq(contents.replace('localhost', 'foobar.com'))
})
})
})
it('injects document.domain on https requests to same superdomain but different subdomain', function () {
return this.setup('https://www.foobar.com:8443')
.then(() => {
evilDns.add('*.foobar.com', '127.0.0.1')
return this.rp({
url: 'https://docs.foobar.com:8443/index.html',
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)
const body = cleanResponseBody(res.body)
expect(body).to.eq('<html><head> <script type=\'text/javascript\'> document.domain = \'foobar.com\'; </script></head><body>https server</body></html>')
})
})
})
it('injects document.domain on other http requests', function () {
nock(this.server._remoteOrigin)
.get('/iframe')
.reply(200, '<html><head></head></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/iframe',
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)
const body = cleanResponseBody(res.body)
expect(body).to.eq('<html><head> <script type=\'text/javascript\'> document.domain = \'google.com\'; </script></head></html>')
})
})
it('injects document.domain on matching super domains but different subdomain', function () {
nock('http://mail.google.com')
.get('/iframe')
.reply(200, '<html><head></head></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://mail.google.com/iframe',
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)
const body = cleanResponseBody(res.body)
expect(body).to.eq('<html><head> <script type=\'text/javascript\'> document.domain = \'google.com\'; </script></head></html>')
})
})
it('does not inject document.domain on non http requests', function () {
nock(this.server._remoteOrigin)
.get('/json')
.reply(200, {
foo: '<html><head></head></html>',
})
return this.rp({
url: 'http://www.google.com/json',
json: true,
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.deep.eq({ foo: '<html><head></head></html>' })
})
})
it('does not inject document.domain on http requests which do not match current superDomain', function () {
nock('http://www.foobar.com')
.get('/')
.reply(200, '<html><head></head><body>hi</body></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.foobar.com',
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.eq('<html><head></head><body>hi</body></html>')
})
})
it('does not inject anything when not text/html response content-type even when __cypress.initial=true', function () {
nock(this.server._remoteOrigin)
.get('/json')
.reply(200, { foo: 'bar' })
return this.rp({
url: 'http://www.google.com/json',
headers: {
'Cookie': '__cypress.initial=true',
'Accept': 'application/json',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq(JSON.stringify({ foo: 'bar' }))
// it should not be telling us to turn this off either
expect(res.headers['set-cookie']).not.to.match(/initial/)
})
})
it('does not inject into x-requested-with request headers', function () {
nock(this.server._remoteOrigin)
.get('/iframe')
.reply(200, '<html><head></head></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/iframe',
headers: {
'Cookie': '__cypress.initial=false',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'X-Requested-With': 'XMLHttpRequest',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq('<html><head></head></html>')
})
})
return ['text/html', 'application/xhtml+xml', 'text/plain, application/xhtml+xml', '', null].forEach((type) => {
it(`does not inject unless both text/html and application/xhtml+xml is requested: tried to accept: ${type}`, function () {
nock(this.server._remoteOrigin)
.get('/iframe')
.reply(200, '<html><head></head></html>', {
'Content-Type': 'text/html',
})
const headers = {
'Cookie': '__cypress.initial=false',
}
headers['Accept'] = type
return this.rp({
url: 'http://www.google.com/iframe',
headers,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const body = cleanResponseBody(res.body)
expect(body).to.eq('<html><head></head></html>')
})
})
})
})
context('security rewriting', () => {
describe('on by default', () => {
beforeEach(function () {
return this.setup('http://www.google.com')
})
it('replaces obstructive code in HTML files', function () {
const html = '<html><body><script>if (top !== self) { }</script></body></html>'
nock(this.server._remoteOrigin)
.get('/index.html')
.reply(200, html, {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/index.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include(
'<script>if (self !== self) { }</script>',
)
})
})
it('replaces obstructive code in JS files', function () {
nock(this.server._remoteOrigin)
.get('/app.js')
.reply(200, 'if (top !== self) { }', {
'Content-Type': 'application/javascript',
})
return this.rp('http://www.google.com/app.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq(
'if (self !== self) { }',
)
})
})
it('handles multi-byte characters correctly when injecting', function () {
const bytes = `0${Array(16 * 1024 * 10).fill('λφ').join('')}`
const response = `<html>${bytes}</html>`
return zlib.gzipAsync(response)
.then((resp) => {
nock(this.server._remoteOrigin)
.get('/index.html')
.reply(200, resp, {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.google.com/index.html',
gzip: true,
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include(`${bytes}</html>`)
})
})
})
// https://github.com/cypress-io/cypress/issues/1396
it('handles multi-byte characters correctly on JS files', function () {
const response = `0${Array(16 * 1024 * 2).fill('λφ').join('')}`
return zlib.gzipAsync(response)
.then((resp) => {
nock(this.server._remoteOrigin)
.get('/index.js')
.reply(200, resp, {
'Content-Type': 'application/javascript',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.google.com/index.js',
gzip: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq(response)
})
})
})
// https://github.com/cypress-io/cypress/issues/1756
// https://github.com/nodejs/node/issues/5761
it('handles gzip responses that are slightly truncated', function () {
const response = 'I am a gzipped response'
return zlib.gzipAsync(response)
.then((resp) => {
nock(this.server._remoteOrigin)
.get('/app.js')
// remove the last 8 characters which
// truncates the CRC checksum and size check
// at the end of the stream
.reply(200, resp.slice(0, -8), {
'Content-Type': 'application/javascript',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.google.com/app.js',
gzip: true,
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq(response)
})
})
})
it('handles gzip responses that are slightly truncated when injecting', function () {
const response = '<html>I am a gzipped response</html>'
return zlib.gzipAsync(response)
.then((resp) => {
nock(this.server._remoteOrigin)
.get('/index.html')
// remove the last 8 characters which
// truncates the CRC checksum and size check
// at the end of the stream
.reply(200, resp.slice(0, -8), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.google.com/index.html',
gzip: true,
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include('I am a gzipped response</html>')
})
})
})
it('ECONNRESETs bad gzip responses when not injecting', function (done) {
nock(this.server._remoteOrigin)
.get('/app.js')
.delayBody(100)
.replyWithFile(200, Fixtures.path('server/gzip-bad.html.gz'), {
'Content-Type': 'application/javascript',
'Content-Encoding': 'gzip',
})
return this.r({
url: 'http://www.google.com/app.js',
gzip: true,
})
.on('error', (err) => {
expect(err.code).to.eq('ECONNRESET')
return done()
})
})
it('ECONNRESETs bad gzip responses when injecting', function () {
nock(this.server._remoteOrigin)
.get('/index.html')
.replyWithFile(200, Fixtures.path('server/gzip-bad.html.gz'), {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip',
})
return this.rp({
url: 'http://www.google.com/index.html',
gzip: true,
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then(() => {
throw new Error('should not reach')
}).catch((err) => {
expect(err.error.code).to.eq('ECONNRESET')
})
})
it('does not die rewriting a huge JS file', function () {
const pathToHugeAppJs = Fixtures.path('server/libs/huge_app.js')
const getHugeFile = () => {
return rp('https://s3.amazonaws.com/internal-test-runner-assets.cypress.io/huge_app.js')
.then((resp) => {
return fs
.outputFileAsync(pathToHugeAppJs, resp)
.return(resp)
})
}
return fs
.readFileAsync(pathToHugeAppJs, 'utf8')
.catch(getHugeFile)
.then((hugeJsFile) => {
nock(this.server._remoteOrigin)
.get('/app.js')
.reply(200, hugeJsFile, {
'Content-Type': 'application/javascript',
})
let reqTime = new Date()
return this.rp('http://www.google.com/app.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
reqTime = new Date() - reqTime
// shouldn't be more than 500ms
expect(reqTime).to.be.lt(500)
})
})
})
})
describe('off with config', () => {
beforeEach(function () {
return this.setup('http://www.google.com', {
config: {
modifyObstructiveCode: false,
},
})
})
it('can turn off security rewriting for HTML', function () {
const html = '<html><body><script>if (top !== self) { }</script></body></html>'
nock(this.server._remoteOrigin)
.get('/index.html')
.reply(200, html, {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/index.html',
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.include(
'<script>if (top !== self) { }</script>',
)
})
})
it('does not replaces obstructive code in JS files', function () {
nock(this.server._remoteOrigin)
.get('/app.js')
.reply(200, 'if (top !== self) { }', {
'Content-Type': 'application/javascript',
})
return this.rp('http://www.google.com/app.js')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq(
'if (top !== self) { }',
)
})
})
})
})
context('FQDN rewriting', () => {
beforeEach(function () {
return this.setup('http://www.google.com')
})
it('does not rewrite html when initial', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html><body><a href=\'http://www.google.com\'>google</a></body></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
headers: {
'Cookie': '__cypress.initial=true',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
const {
body,
} = res
expect(body).to.include('<a href=\'http://www.google.com\'>google</a>')
})
})
it('does not rewrite html when not initial', function () {
nock(this.server._remoteOrigin)
.get('/bar')
.reply(200, '<html><body><a href=\'http://www.google.com\'>google</a></body></html>', {
'Content-Type': 'text/html',
})
return this.rp({
url: 'http://www.google.com/bar',
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)
const {
body,
} = res
expect(body).to.include('<a href=\'http://www.google.com\'>google</a>')
})
})
})
context('file requests', () => {
beforeEach(function () {
Fixtures.scaffold()
return this.setup('/index.html', {
projectRoot: Fixtures.projectPath('no-server'),
config: {
fileServerFolder: 'dev',
integrationFolder: 'my-tests',
},
})
.then(() => {
return this.rp({
url: `${this.proxy}/index.html`,
headers: {
'Cookie': '__cypress.initial=true',
'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.match(/index.html content/)
expect(res.body).to.match(/Cypress\.action/)
expect(res.headers['set-cookie']).to.match(/initial=;/)
expect(res.headers['cache-control']).to.eq('no-cache, no-store, must-revalidate')
expect(res.headers['etag']).to.exist
expect(res.headers['last-modified']).to.exist
})
})
})
it('sets etag', function () {
return this.rp(`${this.proxy}/assets/app.css`)
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('html { color: black; }')
expect(res.headers['etag']).to.be.a('string')
})
})
it('sets last-modified', function () {
return this.rp(`${this.proxy}/assets/app.css`)
.then((res) => {
expect(res.headers['last-modified']).to.be.a('string')
})
})
it('streams from file system', function () {
return this.rp(`${this.proxy}/assets/app.css`)
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('html { color: black; }')
})
})
it('sets content-type', function () {
return this.rp(`${this.proxy}/assets/app.css`)
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.headers['content-type']).to.match(/text\/css/)
})
})
it('disregards anything past the pathname', function () {
return this.rp(`${this.proxy}/assets/app.css?foo=bar#hash`)
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('html { color: black; }')
})
})
it('can serve files with spaces in the path', function () {
return this.rp(`${this.proxy}/a space/foo.txt`)
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('foo')
})
})
it('sets x-cypress-file-path headers', function () {
return this.rp(`${this.proxy}/assets/app.css`)
.then((res) => {
expect(res.headers).to.have.property('x-cypress-file-path', `${Fixtures.projectPath('no-server')}/dev/assets/app.css`)
})
})
it('sets x-cypress-file-server-error headers on error', function () {
return this.rp(`${this.proxy}/does-not-exist.html`)
.then((res) => {
expect(res.statusCode).to.eq(404)
expect(res.headers).to.have.property('x-cypress-file-server-error', 'true')
})
})
it('injects document.domain on other http requests', function () {
return this.rp({
url: `${this.proxy}/index.html`,
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('document.domain = \'localhost\';')
})
})
it('injects document.domain on other http requests to root', function () {
return this.rp({
url: `${this.proxy}/sub/`,
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('document.domain = \'localhost\';')
})
})
it('does not inject injects document.domain on 301 redirects to folders', function () {
return this.rp({
url: `${this.proxy}/sub`,
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(301)
expect(res.body).not.to.include('document.domain = \'localhost\';')
})
})
it('does not inject document.domain on non http requests', function () {
return this.rp({
url: `${this.proxy}/assets/foo.json`,
json: true,
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.deep.eq({ contents: '<html><head></head></html>' })
})
})
it('does not inject anything when not text/html response content-type even when __cypress.initial=true', function () {
return this.rp({
url: `${this.proxy}/assets/foo.json`,
headers: {
'Cookie': '__cypress.initial=true',
'Accept': 'application/json',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.deep.eq(JSON.stringify({ contents: '<html><head></head></html>' }, null, 2))
// it should not be telling us to turn this off either
expect(res.headers['set-cookie']).not.to.match(/initial/)
})
})
})
context('http requests', () => {
beforeEach(function () {
return this.setup('http://getbootstrap.com')
.then(() => {
nock(this.server._remoteOrigin)
.get('/components')
.reply(200, 'content page', {
'Content-Type': 'text/html',
})
return this.rp('http://getbootstrap.com/components')
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
})
it('proxies http requests', function () {
nock(this.server._remoteOrigin)
.get('/assets/css/application.css')
.reply(200, 'html { color: #333 }', {
'Content-Type': 'text/css',
})
return this.rp('http://getbootstrap.com/assets/css/application.css')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('html { color: #333 }')
})
})
})
context('when session is already set', () => {
beforeEach(function () {
return this.setup('http://getbootstrap.com')
.then(() => {
// make an initial request to set the
// session proxy!
nock(this.server._remoteOrigin)
.get('/css')
.reply(200, 'css content page', {
'Content-Type': 'text/html',
})
return this.rp('http://getbootstrap.com/css')
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
})
it('proxies to the remote session', function () {
nock(this.server._remoteOrigin)
.get('/assets/css/application.css')
.reply(200, 'html { color: #333 }', {
'Content-Type': 'text/css',
})
return this.rp('http://getbootstrap.com/assets/css/application.css')
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('html { color: #333 }')
})
})
})
context('localhost', () => {
beforeEach(function () {
return this.setup('http://localhost:6565')
})
it('makes requests to ipv6 when ipv4 fails', function (done) {
// make sure that this test times out relatively fast
// to ensure that requests are fast, the retrying functionality
// does not extend them, and that the server closes quickly
// due to the reduced keep alive timeout
this.timeout(1500)
return dns.lookup('localhost', { all: true }, (err, addresses) => {
if (err) {
return done(err)
}
// if we dont have multiple addresses aka ipv6 then
// just skip this test
if (!_.find(addresses, { family: 6 })) {
return done()
}
// create a server that is only bound to ipv6
// and ensure that it is found by localhost dns lookup
const server = http.createServer((req, res) => {
res.writeHead(200)
return res.end()
})
// reduce this down from 5000ms to 100ms
// so that our server closes much faster
server.keepAliveTimeout = 100
// start the server listening on ipv6 only
// for demo how to bind to localhost via ipv6 see project
// https://github.com/bahmutov/docker-ip6
return server.listen(6565, '::', () => {
return this.rp('http://localhost:6565/#/foo')
.then((res) => {
expect(res.statusCode).to.eq(200)
return server.close(() => {
return done()
})
})
})
})
})
it('handles 204 no content status codes', function () {
nock('http://localhost:6565')
.get('/user/rooms')
.reply(204, '')
return this.rp('http://localhost:6565/user/rooms')
.then((res) => {
expect(res.statusCode).to.eq(204)
expect(res.body).to.eq('')
})
})
})
context('blacklisted hosts', () => {
beforeEach(function () {
return this.setup({
config: {
blacklistHosts: [
'*.google.com',
'shop.apple.com',
'cypress.io',
'localhost:6666',
'*adwords.com',
],
},
})
})
it('returns 503 and custom headers for all hosts', function () {
const expectedHeader = (res, val) => {
expect(res.headers['x-cypress-matched-blacklisted-host']).to.eq(val)
}
return Promise.all([
this.rp('https://mail.google.com/f'),
this.rp('http://shop.apple.com/o'),
this.rp('https://cypress.io'),
this.rp('https://localhost:6666/o'),
this.rp('https://some.adwords.com'),
])
.spread((...responses) => {
_.every(responses, (res) => {
expect(res.statusCode).to.eq(503)
expect(res.body).to.be.empty
})
expectedHeader(responses[0], '*.google.com')
expectedHeader(responses[1], 'shop.apple.com')
expectedHeader(responses[2], 'cypress.io')
expectedHeader(responses[3], 'localhost:6666')
return expectedHeader(responses[4], '*adwords.com')
})
})
})
context('client aborts', () => {
beforeEach(function () {
return this.setup('http://localhost:6565')
})
it('aborts the proxied request', function (done) {
fs
.readFileAsync(Fixtures.path('server/libs/huge_app.js'), 'utf8')
.then((str) => {
const server = http.createServer((req, res) => {
// when the incoming message to our
// 3rd party server is aborted then
// we know we've juggled up the event
// properly
req.on('aborted', () => {
return server.close(() => {
return done()
})
})
res.writeHead(200, {
'Content-Type': 'application/javascript',
})
// write some bytes, causing
// the response event to fire
// on our request
return res.write(str.slice(0, 10000))
})
return server.listen(6565, () => {
const abort = () => {
return r.abort()
}
r = this.r({
url: 'http://localhost:6565/app.js',
})
// abort when we get the
// response headers
.on('response', abort)
})
})
})
})
context('event source / server sent events / SSE', () => {
let onRequest = null
beforeEach(function () {
onRequest = function () {}
return this.setup('http://localhost:5959', {
config: {
responseTimeout: 50,
},
})
.then(() => {
this.srv = http.createServer((req, res) => {
onRequest(req, res)
this.sseStream = new SseStream(req)
return this.sseStream.pipe(res)
})
Promise.promisifyAll(this.srv)
return this.srv.listenAsync()
.then(() => {
this.port = this.srv.address().port
})
})
})
afterEach(function () {
return this.srv.closeAsync()
})
it('receives event source messages through the proxy', function (done) {
onRequest = function (req, res) {
// when the request is finally
// aborted then we know we've closed
// the connection correctly
const closed = new Promise((resolve) => {
return res.on('close', () => {
return resolve()
})
})
const aborted = new Promise((resolve) => {
return req.on('aborted', () => {
return resolve()
})
})
return Promise.join(aborted, closed)
.then(() => {
return done()
})
}
const es = new EventSource(`http://localhost:${this.port}/sse`, {
proxy: this.proxy,
})
es.onopen = () => {
return Promise
.delay(100)
.then(() => {
return this.sseStream.write({
data: 'hey',
})
})
}
es.onmessage = (m) => {
expect(m.data).to.eq('hey')
return es.close()
}
})
})
context('when body should be empty', function () {
this.timeout(1000)
beforeEach(function (done) {
this.httpSrv = http.createServer((req, res) => {
const { query } = url.parse(req.url, true)
if (_.has(query, 'chunked')) {
res.setHeader('tranfer-encoding', 'chunked')
} else {
res.setHeader('content-length', '0')
}
res.writeHead(Number(query.status), {
'x-foo': 'bar',
})
return res.end()
})
return this.httpSrv.listen(() => {
this.port = this.httpSrv.address().port
return this.setup(`http://localhost:${this.port}`)
.then(_.ary(done, 0))
})
})
afterEach(function () {
return this.httpSrv.close()
})
return [204, 304].forEach((status) => {
it(`passes through a ${status} response immediately`, function () {
return this.rp({
url: `http://localhost:${this.port}/?status=${status}`,
timeout: 100,
})
.then((res) => {
expect(res.headers['x-foo']).to.eq('bar')
expect(res.statusCode).to.eq(status)
})
})
it(`passes through a ${status} response with chunked encoding immediately`, function () {
return this.rp({
url: `http://localhost:${this.port}/?status=${status}&chunked`,
timeout: 100,
})
.then((res) => {
expect(res.headers['x-foo']).to.eq('bar')
expect(res.statusCode).to.eq(status)
})
})
})
})
})
context('POST *', () => {
beforeEach(function () {
return this.setup('http://localhost:8000')
})
it('processes POST + redirect on remote proxy', function () {
nock(this.server._remoteOrigin)
.post('/login', {
username: 'brian@cypress.io',
password: 'foobar',
})
.reply(302, undefined, {
'Location': '/dashboard',
})
return this.rp({
method: 'POST',
url: 'http://localhost:8000/login',
form: {
username: 'brian@cypress.io',
password: 'foobar',
},
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.match(/dashboard/)
})
})
// this happens on a real form submit because beforeunload fires
// and initial=true gets set
it('processes POST + redirect on remote initial', function () {
nock(this.server._remoteOrigin)
.post('/login', {
username: 'brian@cypress.io',
password: 'foobar',
})
.reply(302, undefined, {
'Location': '/dashboard',
})
return this.rp({
method: 'POST',
url: 'http://localhost:8000/login',
form: {
username: 'brian@cypress.io',
password: 'foobar',
},
headers: {
'Cookie': '__cypress.initial=true',
},
})
.then((res) => {
expect(res.statusCode).to.eq(302)
expect(res.headers['location']).to.match(/dashboard/)
expect(res.headers['set-cookie']).to.match(/initial=true/)
})
})
it('does not alter request headers', function () {
nock(this.server._remoteOrigin)
.matchHeader('x-csrf-token', 'abc-123')
.post('/login', {
username: 'brian@cypress.io',
password: 'foobar',
})
.reply(200, 'OK')
return this.rp({
method: 'POST',
url: 'http://localhost:8000/login',
form: {
username: 'brian@cypress.io',
password: 'foobar',
},
headers: {
'X-CSRF-Token': 'abc-123',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
expect(res.body).to.eq('OK')
})
})
it('does not fail on a big cookie', function () {
nock(this.server._remoteOrigin)
.post('/login')
.reply(200)
return this.rp({
method: 'POST',
url: 'http://localhost:8000/login',
json: true,
body: {
'query': { 'bool': { 'must': [{ 'filtered': { 'filter': { 'term': { 'brand_id': 3 } } } }, { 'filtered': { 'filter': { 'term': { 'is_live': true } } } }, { 'filtered': { 'filter': { 'bool': { 'should': [{ 'term': { 'subcategory_id': 25 } }, { 'term': { 'subcategory_id': 21 } }] } } } }] } }, 'aggs': { 'colors.raw3': { 'filter': {}, 'aggs': { 'colors.raw': { 'terms': { 'field': 'colors.raw', 'size': 50 } }, 'colors.raw_count': { 'cardinality': { 'field': 'colors.raw' } } } }, 'patterns.raw4': { 'filter': {}, 'aggs': { 'patterns.raw': { 'terms': { 'field': 'patterns.raw', 'size': 50 } }, 'patterns.raw_count': { 'cardinality': { 'field': 'patterns.raw' } } } }, 'custom_filters_a.raw5': { 'filter': {}, 'aggs': { 'custom_filters_a.raw': { 'terms': { 'field': 'custom_filters_a.raw', 'size': 50 } }, 'custom_filters_a.raw_count': { 'cardinality': { 'field': 'custom_filters_a.raw' } } } }, 'custom_filters_b.raw6': { 'filter': {}, 'aggs': { 'custom_filters_b.raw': { 'terms': { 'field': 'custom_filters_b.raw', 'size': 50 } }, 'custom_filters_b.raw_count': { 'cardinality': { 'field': 'custom_filters_b.raw' } } } } }, 'size': 100,
},
headers: {
'Cookie': '_treebook_session=MmxpQUFjak1mZXN1L21FSnY2dzhJaWFPa0kxaXR2MFVMMTFKQnhFMTY0ZGhVZDJLV3BPUm9xT3BWREVDOFExTFBSaDEzY2htQTVVZ0dpZjg0VmF1SmRINlVSZ2ZFeFpzd0s3Yk4wakRxQS9TdW11N3ZURnIvbHAvQ2NTVjZWY0VqdFNQZTFHV09xclZnM0lDNE1JUzZzN3BWamF0dXRSM09uRFZZeWMwd01ESkdWUzY2MFE2QkY0cStIQnBwNGk0V3hhWkNVd0QwamtMeDJheWxxb04wZVkwRzJmdmVLZXJsR3M5UFAyK0Y3ST0tLTBFWmJwWktQaThrWnN6dUZVaVBGckE9PQ%3D%3D--a07f8cd3fc4db9a0c52676e274e71546a9f047fb; _my-site_session=NVp4akFGelljaFZJR3A4US9ES1ZBcTZ4K2ZqNklkUzNKZUVGU3RuQk1HSGZxenU1Z2VDOHBvTmdwL0x3c0E0TGEvTmZDWVR5Y205ZkVOZWwrYkdaWEV2V3k0T1h0elN1NjlsSWo1dFZqVG9nNy9MNWp6enoyZmdYSzYwb3d4WVlvMG9qVXlOTnNNSm1VYlpSSWc3bkFnPT0tLWhQcnVqQ0NQMGF6dVE0SCtRQk1sdmc9PQ%3D%3D--2d25be12c8415195314fe3990cad281c14022d9d; _first_app_session=ajBJcFpRa2dSR29MaTd5UUFnTU45dVZzd2tKeUY2enpuR1cyNFB2bVRCQ201RTh5bXAvaTdlN2NuRm1WeEJOZlkyTWtIRXhpZm1HTlZMM0hPeHVzWUkvQWNhUmNUcUNRRzlDbW8yMlA4SjFZSjhYQ1I4V0FNU3k2Um1mOHlQNU94SkdrT1ZmZS9PZHB6WDVGN0s4cGNnPT0tLTA0aE5hQStvbXQ5T1VXN0UrT3MxN0E9PQ%3D%3D--1daec80639465389f0e5437193e633eeb7bbfca9; _pet_store_session=N055S0M0azJlUlFJWGdDN01MQ3Z4aXFCWHNFU2ttZUp0SDJyL3BPeTVOdzBYWGROb1F1UWx2eG80ZjlobDF2QWJ4Rk5uVFliSWtMSEltZ2RUVDNZUVFLd2VYS3JTQldRbkNnMVdReVY0V1FyWjBNdTVjSk9SQUJNZ0JmMitEcG9JQnh0WVErY2lZaWo4WGJFeXpKUElBPT0tLUZDL2k3bUlKQUl4aGV3ZGEvQXRCaUE9PQ%3D%3D--a658fa7995dec5c7ec3e041d5dc5f52e4f7be0ee; _adventures_session=MDZDeDBDQmRJRFdpYzk5SWFEa0txd0xBVmQyei9xTUhQZ3VlZjNRcUY2c0VsWkZaNUo2SktlNGRvcFNrdDlqL0wzRnA4eVdpMTdMUjJlcHJuMkVWU05SdmNRa29QQU5HWlhxajRZcWNSY1lqR1RDZjhsQ3loUzMvd1M5V3ZCck5iMHRkQXgzcFAwRlBOMFFja2dURDFBPT0tLWMyMHZvcnV6OUIvS0tkeFFpTVA5Qnc9PQ%3D%3D--04a940aa32a208e9bfb6b422481e658f57411132; _classroom_session=NFRSY05TaHZZdkVMYkRZdXh5djQxSmxYY2xIaHJDamI3bGJuRjRxbDZCcWxCMVRxOVpkdWV2MnQ0ajBBNDdOb0ZtTUZPUkFLNmJ0SHFVUlJCZ3FUSjBvSUkxTzZpL1h6ZWhsVlJ6Z2xGRjhWamlVSHJ3NG1vZ3IxSC9PaUxkNVA0Q2x6ZDRTaXJxcGlvc0tsazdkRHFBPT0tLTZ1Z0hxcU16cmZoRTR1VHBqN3RkbXc9PQ%3D%3D--cea92642cc4ec52a81ad6892db90e0e8b8236069; _boo_scaffold_session=am5oQVhMN2ZQQlJBb2VUdDNtdUVic0NMeFlhWVJOa0Y5SEhERjdpUmd2aVZicnY0Z1VsL0hTWVNudTF0dm1SQWNKZHhzZmpJQi9FK1JZVXZwU3Q2RHNObmFWcVg5T0thYmUrY0k2YTJaQjMzUVp3MlRNenk3a2JxczdWZmF6MUxEaW1EejlxeWpUNjFack4rM3N1bm5yZlFKT3BTYnIvTTNNaG5haXZrMEFZWUp2cHBRQnNXbldLUkRZZlBuRDlWdWRldTJqYmh6K0lWWGRyVGpOUERtbFBQUFVHS1pOR3RtMmRuYUxLbjUvYXQra3UwcUxkK3BHRzFRR2RqMWVGay0tQU9DdlFMNEluVVJPTUNlbDh3L1Y0Zz09--c3e2745720ea2080dadaac45223d36ed15fd7fb4; _db_explore_session=RW5NUDlSaG5SVFF4Zms4YmRwQmhZaitrckhRVXN0VzlKS3E3M3Fxa0xwQmZGcDY1amlVOG5DRnpPQ3hLSVBoUFZ5b3lKWGd2Z0JZWnptMEREcU1DUE04SkRMN1RUbVUvUDFaTkNFcUpqQkZ0NmxjTkhrZHZlM1pibmIyTGQxZ3UzNVpvTUlDSnNhcUxBcHRJaVJqdENRPT0tLTBXWVhqK3lwcTVONzVLRVhnZldmQkE9PQ%3D%3D--10c607bc41831a8b4e80689d051a5cfc19872cc1; _furniture_store_session=cm5lTW9XR0ptc2lCSVJNY0c3dW1oVFlJdG9OaGh1OENhdjgyMW5Heis0RlB3ZkNPR2ZZUmdiV1VlbGY3NmdmdHU0cERBZGtpWGFUeHo5cHM4K1lKa053cEhrV2VaR3Q5RVYzZms0VVh2aFBrd2N6U3hzeWhZTEhnVG1qYldxNFZOemVTMGRkQmZQSEFHWEJZZWNQWS9BPT0tLWk1R0d0SW1pZVEyckRGK2R1Vzh4M0E9PQ%3D%3D--99c9ed2f5a43af5bc018d930b6210b50bf973a79; _travel_session=VEcxVlB4NTg0SGNHdjdOekZndW5Jc1FJY2VJd3NTaDhVVk4vNnVuekpWUVE2ekVhK25YVWVUemhOUnl2S3pVWTc3U0trTTI2cUVNakwyd254M3RMT0J2L0dheTdRUVpGY1FRd0FTT3pSaGpsK3kreVJxNVdMbWFsY3pWSVdMUU5mRzRzQnIzRzFNMS8zQThZQStLTE9nPT0tLVNMK2hsYjVpRmZNMngwUFI1QU0zemc9PQ%3D%3D--19113054b718f1b9c733427c9a3e110b60187f4f; _doctors_office_session=RGxTSWhxVm45am0rSGZVb25jRnBVdVRvcHIxbTR3dVZWdjYvYklGY3BVa2FnYm5sRXlNUlp2TmN2Q3R3NndxWnFFdStPT2ZieTVKaE1SMUpFM0ZUaFhUb3ZhbkxDbDlrZk9lUVh5dlRCY0hhQ1NVNUVKSjhJSnhyTkFkWi9FUm1UZis1VEhVb2tXQW9Qb2MrQU5IMHNRPT0tLUMxdlpYMEVKV081L3QveFBHMS9scWc9PQ%3D%3D--7a4196ea6b5194f412be549a0f30925ec6bd118f; _recipe_app_session=OE1saTRQZ1pRQnNrdHNCNllyNld3cnVTcnhqUjh0Q2NZaSt3eWlLbjljUWxMdk5seW9SVnQwcmZ4VVBhdWRDUE5qM1hWSi9QaW9adnVFalJ6VjJFVFFhWm1LZFB1ZXlzRzR1UFpDSks0V01pdWFuWk04bExMeDdvYWFZRU9OQm5qQ2ZKcXRLYUdiZmwvWTYwUCs4bnJBPT0tLVdtYkh4cmFJeHQrZXdoazFGSUo2aGc9PQ%3D%3D--1649c7bd76e8944a0911d9e6a59d5a82aa1a0559; _ajax-sample_session=ZDM2NU8xZTRocmxFUlR5ZjZhMGxUSkgzdmFOcVVrQ1ZUN1ZNMFliRGNuRFQ2SkNvV1pVdUlLRWVoY2dodWxQRjNiek84Y0dhdk44dkN6Sy9naDRsQjFRY2FVMXYzcXc3TkVzY28wMnp4OFdiWnBFMFEvNUltK1ZYUUd2THRMblpLVmJHOFIvMEVQaWpMV2dqamI1ZFBnPT0tLTJ4RURLbjVEYkZaSGpvdDk1RU9sNEE9PQ%3D%3D--f13942ae013f1252c609762922b21b8a233b36ed; _stripe_test_session=RnRoMngvN0IwVjBENElrY0w1eStwOGYxdDgxKzNBbStYaHErMkxRQk9xUGFMenRqU3h3UFVNSGNoKzNWSXZJMVZHMXYxcUoyeEpBMVRQVk9CQktOampnaFdYdUJzYTFjSTJJYlI5SkpzTVdPSVJTNU5GaGR2UVJqa3NNaW1UM1l2N2YxUWVKeEtzOUtneldlVjdNcm1BPT0tLWEyaVVValdCSlJhcmRJUmNXVGNqR3c9PQ%3D%3D--66f51d7d80652f09a3d2c56f94a8bd6380d4972b; _wunderground_session=SFViSXNZeVFMYkp0MXNOWkZiSW5BMG94QzlqOFBIZnBSOGY2S3B4RkIybzFZdFBQeUlZTlAwMjVNRWJqckRRc2R6V1pweWZhdmpqcTgrNWliM3Izbk4xaWo4d05Tc1YvMyt4L2tuNTlIVjlUZUpTZEwxcE0xUXRTTjAwQjI3eVpSM2c4MlRnY21jUEpIcGo4SjNnY0pRPT0tLTc3TlMrZjFJT1U2S1cySm9DN0RDNGc9PQ%3D%3D--accf1fa6ea2a345286abe82acd0e882d9f4f2c40; _ecommerce_app_session=S1JlZ3BYTDZraDdZU3RZUmpCMHR2aTRGaUpvL1BJN0p6MjRqM0krbXplRFdjemhOMnQ0ckp1ZERzTUp2eWQ5Zit2bEVCOGZLRU9pOHJCRHp1Z0Z1UUZtRlVEVnBhaFBielBjckNMVmlXR0dxS1ZabjNKUFdBcVBUR1JSUlUxYWFSemNNem50TzFxam9aU1BTRXpvQkNrWHpMMVdvUEM2MjdWbHh3NVdYU2QxM1ZwMmFWM3RZdjVlSWFnd1o5OWxtb1lMenJiVStKUnRRcHEzdHlSTGUvdDlCcnFXWDF5TTBmdXFPN0VsUHJxVk50dTREeHkzZG9PdERZV2l6WXJIOEpyVnRjU0FVOW9XeWpldzdZVFBXM0xFTktjQWFMbXpuWHlLay9pK0ZhSVZ6dUVsVXJleDYwR090QjdmaTIyb0E5ODh6cWVHaTE2SHFCZ2JrcWFaMmtWTlM0K0lVejNUZVZkQThGVTN4N3VBPS0ta1E2clFzb1VRQS91WEFOYjl2NGdJUT09--c47816c90bd08c466f3f8f60f4db07eb40807b08; _Top5_session=dndqMXRCdFVxbERXWCs4UStiQ1gwc0daWE9aOW1wRndWTHVjSk1kNXR1T2xoOUNwbk1ndGR0T1lqZXBkSUVuN2VPRU05RnNxcGx1aUg2RjJya1dWaUNTekZ4RzZCb0VvdEFNYmhaUHNjZXhmWDFWM3EzK0lVUm4yazhoaXZLZlpxSldqUC9GN1NMNm54NHpyRUpTK0tBPT0tLW5QQldSb2VVVWJ2V2syRDhXVUs1TlE9PQ%3D%3D--3f5773b8063cfd8dc61e917501485c86e625a4a6; _recruiting_session=cnlTQ2h6YWZKZFVPb1Q1cklSVngvK3hTK0M5ckhJUmhheE83QStTRThCeGg0dGpaNGJCMVRrUGFiQXphQm5Pb2FZbGNkV095bGdNNjlMUmRCTmhSa2duNzRzZEppc2JBT0VoSnZIVWlCMVJtWXZmZktJQWgvM3paM2ZKbmZCdWd4MGw0SHF3OGt6b0xJaXhXTzVGSGxaL2ZBNkNPZ0dGL0ZPMklkNXVKQXRTdFY4Y0J4bGx1eTZYWW9QQlpPZ2JsLS02b3NpWmZibGJGd3JhWEg0TUJzYUpBPT0%3D--722ac1ecda4ef81214e52effeef8fe14317c2bd3; _marta_near_me_session=N2MreUZINElveVZMN2NyNkUrUkduZVZBZHdHMVRCcHE1TE02ZXlKeGdJd21iYUlDeGl4aGkwWUNiODZPRVNPTTlGd1ZlRWlJZjZTUHVzZ25qWHdtaTg1NjBkR3hKQ2hjSWUvTElQY0t1azN3UmpJbXQ2T0xiTlpCOVphTkVxcjlrNVVGNXhNcTZaY3dVY0JEcmtUb0FkeWRVQVdtWm5sU3l4NG1tbWQ0Z2lSYXpNSUsvNGt1V0IwV2NQV1RjVEZ5NWpWUG0rQWR4N1FTSzlxcUFLRW1LOFVEY1VzaXd2K0x4OHJnRVV6OVdlNTI0ZFJGUytqbUNmMmI0SWJsVWpnRWhWTTNWRnUzYksvazNTK01Zd3drTWc9PS0tVWxaSG1FeGl5Z2JrcUxXUUl3aDhJUT09--80376da7d66242e7d73d7e1e598b115d22ec59d6; _sassy_session=MTBuOGpOaVlWbDc3NEl5U0t5U0o3OHk3N0VOc3FNbVdnU2xEajBqTGtaQm5sMStQT3E0bTlLZmpRYy9zY2xKOVgrK1hhcVhSc2UvalVWYm9jS2RlMlhMTCtOK1JETFZQRUtnTUxRSmlrOHFNU0l4SmxmSTRYcUIrSHJ4cEJBbUNZMXBCS3NBL3hoYmhJY2xOZlJyaW13PT0tLW5ZOTBjUS92MjJ3bXlNWlRwb2wvZFE9PQ%3D%3D--62667911aa0f694c2215d10622c4a2e6d4d41007; _gf-hackathon_session=Z0xBUTAreU1EM1h4NFluL3BsMUhicE9wa1BtUHdQRWRic2NpOWh3NjIyNnduamRsZGh4UDhFcW83MDhXenhhQ2ZsNloxKzZsa3RxQXcycVVtcUw4Tnh4UVRwTVVqUWJiOExtSnQwcHJFb1BnM3d4eTVjejgza3pPTFV2OEwwWTZ2UE04cmVwc3IvN21wRks3c0NWK1A1dEpYdmVsK1NlTGt4b2JKRkpSOE1Db2oxajE0aHQ4YS8wYXVLNDM0OTBkcXlQUzJUVFlUeFc5dUxuRzNtaVU4YWZUSVBKQVUwM1dkRmFJcUpiR0JlST0tLVJEMzZNUWpmU0hqcFEzbVAyLzlDVFE9PQ%3D%3D--7bd822b0ba783a644ba067a9bc664450c8d8c7bb; _Library_session=eXhPTWVQLzFWcVlzcCtIMVJReDdaSzBLZXFsa3NJeFpzb21RdUYyb1d1OHJGaWdhMmVxV1l2aDFkUHZ3dUhLalRFenN6QWpPVDFnSTFKQUJpaWE2MHJZOXVtaWVGSU95SnVQeE13ZmdoV0FMYlYvSDR1NVRQMld5NTgxYTRnbS9VekZlT1VuNkl0TVVwSDdWMDNsbFZ3PT0tLVpocjJmTjZBSDRueTEwc3VMWHZ5Rmc9PQ%3D%3D--7470bc2a23b85bab792f4a9d2ef7e75438503948; _fittery-project_session=NVdHZ2Y4c3h0VFJDQkQ1d1lmTFlNVnZyeml1RzVoN1NBMnh4OTFCYmdYQXQyS0oyT1M5R2R2WFRSNStmdEhWMVpmcWJJYTR2a1RwNnJ3b0RnTWpKZTE3amtPQVhYeERUVWdVbC9pbjF0U2g4T1FFQzJDb2pjZGFDZnhFUkJqOUtuN05jSG1WSVRaZm4wQUgxNjZNWllnPT0tLXhONHdmQkp6T2RvbEVyL09POU8xK2c9PQ%3D%3D--559898f1567aac047c39765abe134e9c9b5081c7; _rubix-3_0-rails_session=ckZBdUFhOCsvbjBxWGk4SGZyQkJkZGFvdG9ReG9wRVBkTVBrRzljN1d5WXMxbzBJakNXZ01KamdGZTdpbTNiTUZPNktObGppbzZndEdkaE84S1pRU3RubEpsMm1VTndiNlVmYk9oWmN6dHczc3RWWUMwUmg2a1JWN1gzK2lmR3Y0aHFBUEJjTU9qOFo3MlZmSkh1dkVRPT0tLVZuN3FJQnhuZER6UVlmaXUvWm5yaGc9PQ%3D%3D--beae7a3f4cd8c9179965f81f4da275b27c9fd20c; _mainmarket_session=OGNILytyTzFxR1RRd1I3dnJva2V1UWMrYzBBckRkRC9nWmxxWC9MdXhUcUMzV0QxbVFnUVAzdWpXU2l1VFByODNDdWRpUzYyVHVwVTFlamNIaGFiSk9kTkZWSy9kWHpqa0xneC9FeVNOZjNYbDhMVU1UT0dYOElua0NaMTV3dFRoV1RHaGRIS3dMcElTZ0p6K1QxYnVrWlNTRXJMenlkVUZ5aWtnWkpRSFZndEpFMHVNR3gxcFRGYVhKaGVUT3BvVEdKMGRRUmFERlQxUDYxbHRJc29OamZJWVM5UUJSeXZzMDVzaUxzN3JYcWswYXZHb0YxZTFuNlhxYlBleEs4ZFBvekkrOXFOYU9QeUcrUHowZ3FpUG0zaE5NamwvOUppZSs1VWV3NXczbi85M2pGWG5ENnlvd1lCcGc5bEpUTTZBeUkyM2Vyd09Vb0dqSjd6Q2x5Z1g4MllzZzV3TkU2ejZ4ek5lVmtGaHlIcGU5NWlXMlNLazc2N0JvUHorT2o0MURRQVFUbHZLMFBraHAxTWRYZGZmT2gzMUx0aDdkdDNESFdMV1hLTUJZQS9XS3RsSGc5U3FQSzJaUmRjdDBjMi0tQlgyVzJITjVPbURqN2gxK1d5dmdQZz09--121f584b91d060b407193488b5341fc4919bfa28; _blog_app_session=VU0xeHFmL29hRTdBTWl6WExCZ2JGbHFwL2s1Qnh0QXpBajlsU2ZseHpDdjl3TzhHZzl0NHhVSFVLUktqRTRxejlYTXVFMW9rRllZSTJ3NTlUcHZHYkdhaFFObG43bkN5VkgwbmdzRWY0c21ZT3RJM0lFcXpCMHh1YjRqakpMdFFPZzc4b1dXLzRsQWc5QXJPMTdZNmwzajBOeG5kRHduYzdObFZSUDNMakFHUXNmU3k3MFdCYkRncU94cE0zcEVyOEN6dER2aXYwUGo2Mm44NW1Ub29sSzJ2RUxGVmV4V05oM2JUUHVjV1hhRT0tLUM4YUM3MUpPeWtDSEYxMTU1aStyR3c9PQ%3D%3D--f30b07b16552349bfb6d11fbae3afd1cbac32bcf; _todo_list_session=eER5TjRjMWdEL0p4eVY1SHFlcUdCYzhVNldhRk5qclVEWTJCSVFjdkNZcXFvT053VXRpU1NKdG9qb3dFdGdpMUwwcmxXbmJIMXVrbDBxSFVjZTNXcHBIbDRkTXhGaTRmTm90YXZ4d2plSWtuWWlwZGdMSkVMRkliSS8wajhVejJ1MC83Q0h3d2NuaTdmMC9WT3o4NTVnPT0tLWtmUlh5SzFiQ2llZHk0am15d3dzeVE9PQ%3D%3D--5a16fdce0a2c0afe942f70c3203334df3a64d2e8; _tts-reddit_session=WkxWcERrRUJFNURkTGxvR3h5VHpPa2FhWXJhc0RrUElIOFVuT1ZhQ3FGSXhta0ZnVm1GNlVUdnJmOUxIOE9aTUhUcS9hMGY0bFl2K1dyUEYvTlpaNlNjTjV4OHBvWW1YdzM0SFozUkZKQTd5YktMeHI3dXRDV21hN3piaGhyalh6b1EzSExmb0Vod1FRYkhPeU1meFVGaUhPVVM5cllPV3dRT2dsQlM4Ty9vT3VRSkdmcTBsZlFhNXBrYU1CWWlMUE5FSmZDam5DRjlUOGdOMjlHOW9TdVVSYlR4MVlFdWZ2b0dzYTJFajhsMD0tLWY5a1o3U1krKzRUK3pBNmc0bEsyS2c9PQ%3D%3D--458a23790e533b457ebbb80e9dd5bdf76e0de77a; _tweeter_session=ZFpCZHR2dklyd0ozUzJ4ZDBCWjhGZVJ1bFlvNGo0bmdIdkltalN4aDNmY1RsbmlhczR4cThsa3JJUGlFZ0JxcHFiUSsxOU5LbDhVdmtsb2ttWndpUWVMcXYzNEpEbXRpZ3JyOERrQytxMmoxT0FZd1orNDdQOEFGNFFqWllyWmJIdHE1RFF5aVY5MndoOGVmOUpMZTZFbEdJTjk2QzhUQWZXOEJNOCtXbWphM2QwS2NxRkhOUFl6ZTIrMDd6RHFJd2QyTDJkVEh5SmhqSnZvVjZZUllFbU5vOExXQmFGM2ppV2JXSXBXRUo3ZVY5ZG5ycEo0dnRnb0kxeitUT1JNWmhTZnNIMDVvbjV4emxQUDF5b2x2Njk3djREZmNza0NudDB4ZW0yay8xaytYS2QrU0cySzJ0ZzEvUUdhcEZwanBoN3o0OWpsaHVmYTc0dUY3U0R6VDN3PT0tLUE0Zy9mS0VmcGp2bXNDSVJ4UGQ2ZkE9PQ%3D%3D--e1ca4a8f11b7d639e0235fec0644adf6e4576888; _angular_rails_reddit_session=RVQ2anNFSE1qK3VGYjFyMzdTTnRXc1ZDM25JNkNqT25OT1ZTK3RWUXptUWtwSHZwVE1vOE5tUWJML2NKUDZVemRYcThicUpYYzJxejJab3RPanRNR21BSTZVM2NBVFRZeE94OE9QVzVhUTFFdUJ6WmljbTN4eFVQV1VKMkVyY3RjYlE5dkRyZVArelFNSTV5UWdqVTJBPT0tLTN4dUlHaVZXcW1wZFNER1JyOFBxWlE9PQ%3D%3D--67046be80e6a383c4ecf3d47bfb8b5d58f5ed7d1; _fittery_session=b1FVdGtyVTN5Sy9MOWdHTEJzU0RxYjBEeERteC9tTTd3ekFOZE02eXV3VHNTM29XV0F4dVhUb1ppTzNmdHZKZGRUSWVpU0lYTm1vQ045TFFCWDhKZldQTFJ1cWpvTkFiZlBIQnVuL0I4SGhIODA0bFNwK1diOVJXVnZ0MzNaekovcEN3dDI2cHdjN3lUSnVlVzdoYjY5a04wYXJiQk41aTc5TGVleExwZkZzdExGTDJrejJtTktiL0p4K3FreENBYnlPTW1WWXFSbEZMWWtidFdzMldDSDVyaG1acEM0OVhxREVlMUg0S0xWcGV2Q0dxTzNaODZWWmhvWWFrNW55SEJ3ZmtEMjR1YlpYSk5uV1Flem4wUjV4U0hSSUtyQlF0QUcvQkc5TGtBWUl1UFRNNEhzLzgrVlg0RmxMcGw3QW42UDVmUjVqQ1hPd0V1K1M1aXFFb0NnPT0tLUw1K0dHZXh6b3pPMDBNT1YwZFEyVFE9PQ%3D%3D--527a62a03427f1ed30709c0a8a591cadb7a26cea; _ladies-of-tts_session=OHEwdWNLVFJyRU8yQTEyVjZyNUp6YzdyUjNJSlVreU15dktVRk1QNlEzQ0xRcUFTUUx3M0hlTEZlUnE5MkNadmZwaWtqTUp0SVRQaGFZcHZ1TDh3am5wZ3J3MnBMaHVFOG5oeDZXTDBRb2VHcmVKZS9ORUpHSmFmcHg0V0dWKzdESGRkZnpZUGVoM3lvQU02VVBaalBnPT0tLVRPbTNQSkY3VUczUUk5UnQ1OUtZNkE9PQ%3D%3D--f48ce24828b13200003ee27dcc8717925dcfc9ce; _three-good-things_session=RWZ0NS92QVN2dWoxWkpzVk1RNEN6UmlXMlFhSnYyc0pXd1l1Q2NzMGFQM1BmMkJlM1gzMG9LeTFKY3I0L2NoUzJwRk1PZnprQUx6U3duUStYYkFBT055WkVRVVIxdlVkalFUaVNBaEdqSW9EM1ZSMUR5d0pUbDgxcUJ5cnZ5cDY5YkFhUlJYTStzZ0JINENTbnFjK2tGU1IxZEg2TWlnUGtuUjlvRXozbnVrcFdEZFlOQWdTREFVTCtOTHRhUW1MZDFWMXVSSDZwdXNmQWlHRlk1Q3ZsWmJWbjMwb0NjRDhNQk90L0YyY1RRST0tLVY2ZnZRemluWWNkY2ZyNTJONEpWNmc9PQ%3D%3D--0e7bf64b6d71d668d845f98f35c1f94d239aefc3; _my_site_session=MFE2NWZydk1Fa05PRHpwbWdDNzl3TXJZZHFEa01pdEFSQUdsSWpzN0F0dGx6cklacFZzV2hsaTdITWpubU5PUDdYU1ZZWkZEOUpWb1pIbDUyeDVHR0ZiNDZHU0U4K2V4QnU0K2pRTmdpdWNlUHFvTnZMQ3g3MHBDSEhjV2VKOWRoTEVFUm9NRWdIUzZ1YjFIaE9aVVlnPT0tLWpjVUVuaHJyM0JoWm1GUit0WmFWL2c9PQ%3D%3D--d7711f08ae193bd0528e10198728887b9eedc426; _rails-starter-kit_session=NmRveUJiZ0RMTTlRR003L1RrUE5lem5oMmRjdG5xMmtZcDRlUTFWRjBXNmNPMkFvT1RqSFBPd1pBQVBHSHRLSmg3Qk53WXZ3Ykp6bkQvT2lqa2dyQU5ZYXlJcXBnaEdabzhTSEJteHl5K1lBNzU2R1BMNGx6WGpFa1dOTVpOUS9mZ09LZWJYZlQwMjhRL2xiWFBnVU9BPT0tLUthVEdXNDlQZDREQTd0MWVkU093SUE9PQ%3D%3D--41cea213452b8234f53415d44c98bf55997633be; step-1=60; step-2=140; step-3=0; step-4=13; step-5=30; step-6=26; step-7=26; step-8=0; current-step=9; step-9=0; sid=111; __cypress.initial=false; __cypress.remoteHost=http://localhost:8000; best_fit=14 1/2, 34-35, Traditional',
},
})
.then((res) => {
expect(res.statusCode).to.eq(200)
})
})
it('hands back 201 status codes', function () {
nock(this.server._remoteOrigin)
.post('/companies/validate', {
payload: { name: 'Brian' },
})
.reply(201)
return this.rp({
method: 'POST',
url: 'http://localhost:8000/companies/validate',
json: true,
body: {
payload: { name: 'Brian' },
},
headers: {
'Cookie': '__cypress.initial=false',
},
})
.then((res) => {
expect(res.statusCode).to.eq(201)
})
})
})
})